Как мне написать это на Ruby/Python?Или вы можете перевести мой LINQ на Ruby/Python?

StackOverflow https://stackoverflow.com/questions/122033

Вопрос

Вчера я спросил этот вопрос и так и не получил ответа, который меня очень порадовал.Мне действительно хотелось бы знать, как сгенерировать список из N уникальных случайных чисел, используя функциональный язык, такой как Ruby, без необходимости быть крайне императивным по стилю.

Поскольку я не увидел ничего, что мне действительно понравилось, я написал искомое решение в LINQ:


       static void Main(string[] args)
        {
            var temp = from q in GetRandomNumbers(100).Distinct().Take(5) select q;
        }

        private static IEnumerable GetRandomNumbers(int max)
        {
            Random r = new Random();
            while (true)
            {
                yield return r.Next(max);
            }
        }

Можете ли вы перевести мой LINQ на Ruby?Питон?Какой-нибудь другой функциональный язык программирования?

Примечание: Пожалуйста, постарайтесь не использовать слишком много циклов и условий — в противном случае решение будет тривиальным.Кроме того, я бы предпочел увидеть решение, в котором вам не нужно создавать массив, намного больший, чем N, чтобы затем вы могли просто удалить дубликаты и обрезать его до N.

Я знаю, что я придирчив, но мне бы очень хотелось увидеть элегантное решение этой проблемы.Спасибо!

Редактировать:
Почему все отрицательные голоса?

Первоначально в моем образце кода после Take() был Distinct(), что, как многие отмечали, могло оставить меня с пустым списком.Я изменил порядок вызова этих методов, чтобы отразить то, что я имел в виду в первую очередь.

Извинения:
Мне сказали, что этот пост выглядит довольно снобистским.Я не пытался сказать, что LINQ лучше Ruby/Python;или что мое решение намного лучше, чем у всех остальных.Моя цель — просто научиться делать это (с некоторыми ограничениями) в Ruby.Мне жаль, если я посчитал себя придурком.

Это было полезно?

Решение

В Руби:

a = (0..100).entries.sort_by {rand}.slice! 0, 5

Обновлять:Вот немного другой способ:a = (0...100).entries.sort_by{rand}[0...5]

РЕДАКТИРОВАТЬ:

и в Ruby 1.9 вы можете сделать это:

Array(0..100).sample(5) 

Другие советы

>>> import random
>>> print random.sample(xrange(100), 5)
[61, 54, 91, 72, 85]

Это должно дать 5 уникальных значений в диапазоне. 0 — 99xrange объект генерирует значения по запросу, поэтому память не используется для значений, которые не выбираются.

Хм...Как насчет (Python):

s = set()
while len(s) <= N: s.update((random.random(),))

Я откажусь от простейших решений с использованием «случайного» модуля, поскольку считаю, что это не совсем то, что вам нужно.Вот что, я думаю, вы ищете в Python:

>>> import random
>>> 
>>> def getUniqueRandomNumbers(num, highest):
...     seen = set()
...     while len(seen) < num:
...         i = random.randrange(0, highest)
...         if i not in seen:
...             seen.add(i)  
...             yield i
... 
>>>

Чтобы показать вам, как это работает:

>>> list(getUniqueRandomNumbers(10, 100))
[81, 57, 98, 47, 93, 31, 29, 24, 97, 10]

Вот еще одно решение Ruby:

a = (1..5).collect { rand(100) }
a & a

Я думаю, что с помощью вашего оператора LINQ Distinct удалит дубликаты после того, как 5 уже были заняты, поэтому вы не можете гарантировать, что 5 получите обратно.Хотя кто-то может меня поправить, если я ошибаюсь.

РЕДАКТИРОВАТЬ :Хорошо, просто ради интереса, короче и быстрее (и все еще с использованием итераторов).

def getRandomNumbers(max, size) :
    pool = set()
    return ((lambda x :  pool.add(x) or x)(random.randrange(max)) for x in xrange(size) if len(a) < size)

print [x for x in gen(100, 5)]
[0, 10, 19, 51, 18]

Да, я знаю, остроты следует оставить любителям Perl, но я думаю, что это довольно мощно, не так ли?

Старое сообщение здесь:

Боже мой, как все это сложно!Давайте будем питоническими:

import random
def getRandomNumber(max, size, min=0) :
   # using () and xrange = using iterators
   return (random.randrange(min, max) for x in xrange(size))

print set(getRandomNumber(100, 5)) # set() removes duplicates
set([88, 99, 29, 70, 23])

Наслаждаться

РЕДАКТИРОВАТЬ :Как заметили комментаторы, это точный перевод кода вопроса.

Чтобы избежать проблемы, возникшей при удалении дубликатов после создания списка, в результате чего данных становится слишком мало, вы можете выбрать другой способ:

def getRandomNumbers(max, size) :
    pool = []
    while len(pool) < size :
        tmp = random.randrange(max)
        if tmp not in pool :
            yield pool.append(tmp) or tmp

print [x for x in getRandomNumbers(5, 5)]
[2, 1, 0, 3, 4]

В Руби 1.9:

Array(0..100).sample(5)

Python с числовым Python:

from numpy import *
a = random.random_integers(0, 100, 5)
b = unique(a)

Вуаля!Конечно, вы могли бы сделать что-то подобное в стиле функционального программирования, но...почему?

import random

def makeRand(n):
   rand = random.Random()
   while 1:
      yield rand.randint(0,n)
   yield rand.randint(0,n)      

gen = makeRand(100)      
terms = [ gen.next() for n in range(5) ]

print "raw list"
print terms
print "de-duped list"
print list(set(terms))

# produces output similar to this
#
# raw list
# [22, 11, 35, 55, 1]
# de-duped list
# [35, 11, 1, 22, 55]

Что ж, сначала вы перепишете LINQ на Python.Тогда ваше решение однострочное :)

from random import randrange

def Distinct(items):
    set = {}
    for i in items:
        if not set.has_key(i):
            yield i
            set[i] = 1

def Take(num, items):
    for i in items:
        if num > 0:
            yield i
            num = num - 1
        else:
            break

def ToArray(items):
    return [i for i in items]

def GetRandomNumbers(max):
    while 1:
        yield randrange(max)

print ToArray(Take(5, Distinct(GetRandomNumbers(100))))

Если вы поместите все описанные выше простые методы в модуль LINQ.py, вы сможете произвести впечатление на своих друзей.

(Отказ от ответственности:конечно, это не на самом деле переписывание LINQ на Python.У людей сложилось ошибочное представление, что LINQ — это просто набор тривиальных методов расширения и какого-то нового синтаксиса.Однако по-настоящему продвинутой частью LINQ является автоматическая генерация SQL, поэтому при запросе к базе данных именно база данных реализует Distinct(), а не клиентская часть.)

Вот транслитерация вашего решения для Python.

Во-первых, генератор, создающий случайные числа.Это не очень Pythonic, но хорошо сочетается с вашим примером кода.

>>> import random
>>> def getRandomNumbers( max ):
...     while True:
...             yield random.randrange(0,max)

Вот клиентский цикл, который собирает набор из 5 различных значений.Это, опять же, не самая Pythonic реализация.

>>> distinctSet= set()
>>> for r in getRandomNumbers( 100 ):
...     distinctSet.add( r )
...     if len(distinctSet) == 5: 
...             break
... 
>>> distinctSet
set([81, 66, 28, 53, 46])

Непонятно, почему вы хотите использовать генератор случайных чисел — это одна из немногих вещей, которые настолько просты, что генератор не упрощает их.

Более питоническая версия может выглядеть примерно так:

distinctSet= set()
while len(distinctSet) != 5:
    distinctSet.add( random.randrange(0,100) )

Если требуется сгенерировать 5 значений и найти различные среди этих 5, то что-то вроде

distinctSet= set( [random.randrange(0,100) for i in range(5) ] )

Возможно, это подойдет вашим потребностям и будет выглядеть немного более лаконично:

from numpy import random,unique

def GetRandomNumbers(total=5):
    while True:
        yield unique(random.random(total*2))[:total]

randomGenerator = GetRandomNumbers()

myRandomNumbers = randomGenerator.next()

Вот еще одна версия Python, более точно соответствующая структуре вашего кода C#.Встроенной функции для получения четких результатов не существует, поэтому я добавил для этого функцию.

import itertools, random

def distinct(seq):
    seen=set()
    for item in seq:
        if item not in seen:
            seen.add(item)
            yield item

def getRandomNumbers(max):
    while 1:
        yield random.randint(0,max)

for item in itertools.islice(distinct(getRandomNumbers(100)), 5):
    print item

Я не умею читать ваш LINQ, но думаю, вы пытаетесь получить от 5 случайных чисел до 100, а затем удалить дубликаты.

Вот решение для этого:

def random(max)
    (rand * max).to_i
end

# Get 5 random numbers between 0 and 100
a = (1..5).inject([]){|acc,i| acc << random( 100)}
# Remove Duplicates
a = a & a

Но, возможно, вы на самом деле ищете 5 различных случайных чисел от 0 до 100.В таком случае:

def random(max)
    (rand * max).to_i
end

a = []
while( a.size < 5)
    a << random( 100)
    a = a & a
end

Это может нарушить ваше представление о том, что «циклов не слишком много», но, по-видимому, Take и Distinct просто скрывают от вас циклы.Было бы достаточно просто добавить методы в Enumerable, чтобы скрыть цикл while.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top