如何在Ruby / Python中编写此代码?或者,我们可以将我的LINQ转换为Ruby / Python吗?
昨天,我问了这个问题,但从未得到真正令我满意的答案。我真的很想知道如何使用诸如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中(有一定限制)进行此操作。对不起,如果我遇到一个混蛋。
解决方案
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对象根据请求生成值,因此没有内存用于未采样的值。
我无法真正阅读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循环就足够了。
嗯...怎么样(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个。如果我错了,有人可以纠正我。
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()的数据库,而不是客户端。)
编辑:好吧,只是为了好玩,一个较短和更快的(并且仍在使用迭代器)。
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]
这是从解决方案到Python的音译。
首先,一个生成随机数的生成器。这不是Python风格,但是与示例代码非常匹配。
>>> 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) ] )
也许这会满足需求,并且看起来有点偏轻:
from numpy import random,unique def GetRandomNumbers(total=5): while True: yield unique(random.random(total*2))[:total] randomGenerator = GetRandomNumbers() myRandomNumbers = randomGenerator.next()
这是另一个python版本,与Ccode的结构更加匹配。没有内置功能可以提供不同的结果,因此我添加了一个函数来执行此操作。
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)