8

昨天,我问了这个问题,但从来没有真正得到我真正满意的答案。我真的很想知道如何使用诸如 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 吗?Python?还有其他函数式编程语言吗?

注意:请尽量不要使用太多的循环和条件 - 否则解决方案是微不足道的。另外,我宁愿看到一个解决方案,您不必生成比 N 大得多的数组,这样您就可以删除重复项并将其修剪为 N。

我知道我很挑剔,但我真的很想看到一些优雅的解决方案来解决这个问题。谢谢!

编辑:
为什么所有的反对票?

最初我的代码示例在 Take() 之后有 Distinct(),正如许多人指出的那样,这可能会给我留下一个空列表。我已经更改了调用这些方法的顺序,以反映我最初的意思。

道歉:
有人告诉我这篇文章相当势利。我并不是要暗示 LINQ 比 Ruby/Python 更好。或者我的解决方案比其他人的解决方案要好得多。我的目的只是学习如何在 Ruby 中做到这一点(有一定的限制)。如果我被认为是个混蛋,我很抱歉。

4

14 回答 14

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

这应该在 range 中产生 5 个唯一值0 — 99。该xrange对象根据请求生成值,因此没有内存用于未采样的值。

于 2008-09-23T16:18:19.447 回答
5

在红宝石中:

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) 
于 2008-09-23T16:27:31.920 回答
3

嗯...怎么样(Python):

s = set()
while len(s) <= N: s.update((random.random(),))
于 2008-09-23T16:24:18.730 回答
2

我将放弃使用“随机”模块的最简单的解决方案,因为我认为这并不是你真正想要的。这是我认为您在 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]
于 2008-09-23T16:17:48.713 回答
2

这是另一个 Ruby 解决方案:

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

我认为,使用您的 LINQ 语句, Distinct 将在 5 个已被占用后删除重复项,因此您不能保证获得 5 个。不过,如果我错了,有人可以纠正我。

于 2008-09-23T16:28:02.340 回答
2

编辑:好的,只是为了好玩,一个更短更快的(并且仍在使用迭代器)。

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]
于 2008-09-23T16:38:45.497 回答
1

在 Ruby 1.9 中:

Array(0..100).sample(5)
于 2010-09-25T09:17:24.077 回答
0

带有数字 Python 的 Python:

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

瞧!当然你可以用函数式编程风格做类似的事情,但是......为什么?

于 2008-09-23T16:13:51.157 回答
0
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]
于 2008-09-23T16:31:02.240 回答
0

好吧,首先你用 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() 而不是客户端的数据库。)

于 2008-09-23T16:32:42.470 回答
0

这是从您的解决方案到 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])

目前尚不清楚为什么要使用随机数生成器——这是少数几件如此简单以至于生成器无法简化它的事情之一。

更 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) ] )
于 2008-09-23T16:42:27.577 回答
0

也许这会满足您的需求,并且看起来更 linqish:

from numpy import random,unique

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

randomGenerator = GetRandomNumbers()

myRandomNumbers = randomGenerator.next()
于 2008-09-23T16:57:09.180 回答
0

这是另一个 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
于 2008-09-23T19:33:19.820 回答
-1

我无法真正阅读您的 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

但也许您实际上是在寻找 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 循环就很容易了。

于 2008-09-23T16:23:30.090 回答