Вчера я задал вопрос это и так и не получил ответа, которым я был бы очень доволен. Я действительно хотел бы знать, как сгенерировать список из 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? Python? Любой другой функциональный язык программирования?
Примечание: Пожалуйста, постарайтесь не использовать слишком много циклов и условных выражений - иначе решение тривиально. Кроме того, я бы предпочел увидеть решение, в котором вам не нужно создавать массив, намного превышающий N, поэтому вы можете просто удалить дубликаты и обрезать его до N.
Я знаю, что разборчив, но мне бы очень хотелось увидеть несколько изящных решений этой проблемы. Спасибо!
Редактировать:
Почему все отрицательные?
Первоначально в моем образце кода после Take () был Distinct (), который, как многие отмечали, мог оставить меня с пустым списком. Я изменил порядок, в котором вызываются эти методы, чтобы отразить то, что я имел в виду в первую очередь.
Извинения:
Мне сказали, что этот пост произвел впечатление снобизма. Я не пытался сказать, что LINQ лучше Ruby / Python; или что мое решение намного лучше, чем у всех остальных. Моя цель - просто научиться делать это (с определенными ограничениями) в Ruby. Извините, если я показался придурком.
Я не понимаю требований. Нужно ли взять N значений и найти отдельные значения в этом наборе? Или нужно найти набор некоторого размера, который имеет N различных значений?
Если вы возьмете (5), а затем разделите ... вы можете получить 1 число.
@ Дэвид: Да, я только что изменил их порядок. Спасибо!
Готов поспорить, это подразумевает, что LINQ настолько явно превосходит, что никто никогда не сможет создать Ruby или Python, которые соответствуют вашим высоким стандартам «элегантности». Просто догадываюсь. Вопрос хромой, а не хромой который.
Я действительно не имел в виду этого - я только вчера начал изучать Ruby и действительно понятия не имел, как это сделать. Мне очень жаль, если я наткнулся на это.






Python с числовым Python:
from numpy import *
a = random.random_integers(0, 100, 5)
b = unique(a)
Вуаля! Конечно, вы могли бы сделать что-то подобное в стиле функционального программирования, но ... почему?
Потому что это не использует итераторы и сохраняет все целые числа в памяти.
Я откажусь от простейших решений с использованием «случайного» модуля, поскольку я считаю, что это не совсем то, что вам нужно. Вот что, я думаю, вы ищете в 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]
>>> import random
>>> print random.sample(xrange(100), 5)
[61, 54, 91, 72, 85]
Это должно дать 5 уникальных значений в диапазоне 0 — 99. Объект xrange генерирует значения по запросу, поэтому память не используется для значений, которые не были выбраны.
xrange () фактически не использует генератор. это поддельный список. Генераторы не являются последовательностями и не могут быть проиндексированы, поэтому random.sample () потерпит неудачу, если будет таковой.
Почему образцы должны быть уникальными? Кажется, не существует какого-либо фильтра, обеспечивающего уникальность.
Функция random.sample () делает это сама. «Вернуть список уникальных элементов длиной k, выбранных из последовательности популяции».
Я не могу прочитать ваш 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.
Верно: я понимаю, что Take & Distinct, вероятно, крутятся за кадром. Я имел в виду отсутствие цикла, который вам нужно было бы написать ... Тем не менее, мне нравятся ваши вторые решения. Спасибо!
(rand * max).to_i должен быть записан как rand max.
Хм ... Как насчет (Python):
s = set()
while len(s) <= N: s.update((random.random(),))
В 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)
Вот еще одно решение Ruby:
a = (1..5).collect { rand(100) }
a & a
Я думаю, что с вашим оператором LINQ Distinct удалит дубликаты после того, как 5 уже были приняты, поэтому вам не гарантировано вернуть 5. Но кто-то может поправить меня, если я ошибаюсь.
Да, меня это тоже волновало. Однако мне пока не удалось создать массив с менее чем 5 элементами. Однако это не значит, что это могло произойти. В худшем случае я мог бы вызвать Distinct () раньше Take (), верно?
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 (), а не на стороне клиента.)
Хороший. Небольшой комментарий: вы должны использовать set () вместо хеша в Distince, а ToArray может использовать list ().
Обновлено: Хорошо, просто для удовольствия, более короткий и быстрый (и все еще с использованием итераторов).
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]
Если вы удалили дубликат, не получите ли вы меньше значений, чем хотелось бы?
Да, но он сделал то же самое в своем вопросе, так что это точный перевод. Вот что требуется: перевод.
Он этого не сделал - .Take (5) происходит после вызова .Distinct, поэтому будет отрисовываться 5 элементов из уже унифицированной последовательности.
Ничего страшного - просто прочтите комментарий к вопросу, он исправил порядок .Distinct / .Take с момента публикации
Вот транслитерация вашего решения на Python.
Во-первых, генератор, создающий случайные числа. Это не очень похоже на Pythonic, но хорошо сочетается с вашим примером кода.
>>> import random
>>> def getRandomNumbers( max ):
... while True:
... yield random.randrange(0,max)
Вот клиентский цикл, который собирает набор из 5 различных значений. Это - опять же - не самая питоническая реализация.
>>> distinctSet= set()
>>> for r in getRandomNumbers( 100 ):
... distinctSet.add( r )
... if len(distinctSet) == 5:
... break
...
>>> distinctSet
set([81, 66, 28, 53, 46])
Непонятно, почему вы хотите использовать генератор случайных чисел - это одна из немногих вещей, которые настолько просты, что генератор не упрощает ее.
Более Pythonic-версия может выглядеть примерно так:
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
В Ruby 1.9:
Array(0..100).sample(5)
Именно поэтому мы ясно понимаем это: хотя Python имеет некоторые функциональные конструкции, такие как понимание списков, на самом деле это не функциональный язык, и это не та проблема, которую вы легко могли бы решить в истинном функциональном стиле в Python.