これをRuby/Pythonでどうやって書くのでしょうか?それとも、私の LINQ を Ruby/Python に翻訳してもらえますか?
-
02-07-2019 - |
質問
昨日、私は尋ねました これ と質問しましたが、本当に満足のいく答えは得られませんでした。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 でこれを (特定の制約付きで) 行う方法を学ぶことだけです。バカに思われたらごめんなさい。
解決
ルビーの場合:
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 愛好家に任せるべきだと思いますが、これは非常に強力だと思いますね。
古いメッセージはこちら:
いやあ、なんて複雑なことでしょう!Pythonっぽくなりましょう:
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 と数値 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]
そうですね、まず 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 というモジュールに組み込むと、友達を感心させることができます。
(免責事項:もちろん、これはそうではありません 実は LINQをPythonで書き直す。LINQ は単純な拡張メソッドと新しい構文の集まりであるという誤解を持っている人がいます。ただし、LINQ の本当に高度な部分は SQL の自動生成です。そのため、データベースにクエリを実行するときに、クライアント側ではなくデータベースが Distinct() を実装します。)
これは、ソリューションから Python への音訳です。
まず、乱数を生成するジェネレーターです。これはあまり Python 的ではありませんが、サンプル コードとよく一致しています。
>>> import random
>>> def getRandomNumbers( max ):
... while True:
... yield random.randrange(0,max)
これは 5 つの個別の値のセットを収集するクライアント ループです。これは、繰り返しになりますが、最も Python らしい実装ではありません。
>>> distinctSet= set()
>>> for r in getRandomNumbers( 100 ):
... distinctSet.add( r )
... if len(distinctSet) == 5:
... break
...
>>> distinctSet
set([81, 66, 28, 53, 46])
乱数にジェネレーターを使用する理由は明らかではありません。これは、ジェネレーターを使用しても単純化できない数少ない簡単なことの 1 つです。
より Python 的なバージョンは次のようになります。
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()
これは、C# コードの構造とより厳密に一致する別の Python バージョンです。明確な結果を与えるための組み込み機能はないので、これを行うための関数を追加しました。
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 までの 5 つの乱数を取得して重複を削除しようとしていると思います。
その解決策は次のとおりです。
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 ループを非表示にするだけで十分簡単です。