昨天,我问了一个这个问题,从来没有真正得到答案,我真的很高兴。我真的想知道如何使用诸如Ruby之类的函数语言生成N个唯一随机数的列表,而不必在风格上非常强制。

由于我没有看到任何我真正喜欢的内容,我已经在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中执行此操作(具有某些约束)。如果我是一个混蛋,我很抱歉。

有帮助吗?

解决方案

在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]

这应该在 0&#8212;范围内产生5个唯一值。 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爱好者,但我认为这个非常强大不是吗?

此处留言:

天哪,这一切有多复杂!让我们成为pythonic:

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]

在Ruby 1.9中:

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

Python with Numeric Python:

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

Voil&#224 ;!当然你可以在函数式编程风格中做类似的事情但是......为什么?

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]

好吧,首先你用Python重写LINQ。然后你的解决方案就是单线:)

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的模块中,您可以给朋友留下深刻印象。

(免责声明:当然,这不是实际在Python中重写LINQ。人们误以为LINQ只是一堆简单的扩展方法和一些新的语法.LINQ的真正高级部分但是,它是自动生成SQL,因此当您查询数据库时,它是实现Distinct()而不是客户端的数据库。)

这是从您的解决方案到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个值并在这些值中找到不同的值,那么就像

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,但我认为您正在尝试获取最多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

但也许你实际上在寻找0到100之间的5个不同的随机数。在这种情况下:

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