Как мне написать это на Ruby/Python?Или вы можете перевести мой LINQ на Ruby/Python?
-
02-07-2019 - |
Вопрос
Вчера я спросил этот вопрос и так и не получил ответа, который меня очень порадовал.Мне действительно хотелось бы знать, как сгенерировать список из 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 — 99
.А xrange
объект генерирует значения по запросу, поэтому память не используется для значений, которые не выбираются.
Хм...Как насчет (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.