Bash'ta büyük, düzgün dağılmış, rastgele tamsayılar verimli bir şekilde nasıl üretilir?


30

Ben almak için en iyi yol olacağını merak edilmiş iyi bir prosedür arasında rastgele pozitif bir tamsayı olsun ne olurdu yani bash, tesadüfîliği, MINve MAXöyle ki

  1. Aralık isteğe bağlı olarak büyük olabilir (veya en azından 2 32 - 1'e kadar);
  2. Değerler eşit olarak dağılmıştır (yani önyargı yok);
  3. Bu verimli.

Bashta rasgeleliği elde etmenin etkili bir yolu $RANDOMdeğişkeni kullanmaktır . Bununla birlikte, bu sadece 0 ile 2 15 - 1 arasında, tüm amaçlar için yeterince büyük olmayabilecek bir değeri örnekler . İnsanlar genellikle istedikleri menzile girmek için bir modülo kullanırlar, örn.

MIN=0
MAX=12345
rnd=$(( $RANDOM % ($MAX + 1 - $MIN) + $MIN ))

Bu, ayrıca, $MAX2 15 -1 = 32767 bölmediği sürece bir önyargı yaratır . Örneğin, eğer $MIN0 ve $MAX, sonra 7'ye kadar değerler 0 hafifçe değerler 8 ve 9 daha olası olduğunu, 9 $RANDOMasla 32768 veya 32769. Bu önyargı, mesela menzil arttıkça kötüleşir $MIN0 ve $MAXolduğu 9999'da, 0 ila 2767 arasındaki sayılar 4 / 32767'lik bir olasılığa sahipken 2768 ila 9999 arasındaki sayılar yalnızca 3 / 32767'lik bir olasılığa sahiptir .

Dolayısıyla yukarıdaki yöntem koşul 3'ü yerine getirirken, koşul 1 ve 2'yi yerine getirmez.

1. ve 2. koşulları yerine getirmeye çalıştığım en iyi yöntem şu şekilde kullanmaktı /dev/urandom:

MIN=0
MAX=1234567890
while
  rnd=$(cat /dev/urandom | tr -dc 0-9 | fold -w${#MAX} | head -1 | sed 's/^0*//;')
  [ -z $rnd ] && rnd=0
  (( $rnd < $MIN || $rnd > $MAX ))
do :
done

Temel olarak, sadece /dev/urandom( /dev/randomeğer kriptografik olarak güçlü bir yalancı sayı üreteci isteniyorsa ve bunun yerine çok fazla zamanınız varsa veya donanımsal rastgele sayı üreteci varsa) kullanmayı düşünebilirsiniz ), ondalık basamak olmayan her karakteri silin. $MAX0'ın çıkış uzunluğu ve kesilir. Biz sadece 0 almaya çalıştığım olduysa o $rndyüzden bu durum kümesinde, boş rndiçin 0. Sonucun aralığımızın dışında olup olmadığını kontrol edin ve öyleyse tekrarlayın. Bu süre döngüsünün "bedenini", bir do ... whiledöngü taklit etmek ruhu içinde, en rndbaştan tanımsız olduğundan , vücudun en az bir kez uygulanmasını zorlamak için burada korumaya zorladım .

Sanırım burada 1. ve 2. koşulları yerine getirdim, ama şimdi 3. durumu batırdım. Bir saniye kadar sürer (şanslı olduğumda saniyenin onda biri). Aslında, döngünün sonlandırılması bile garanti edilmez (sonlandırma olasılığı zaman arttıkça 1'e yaklaşır).

Önceden belirlenmiş ve potansiyel olarak geniş bir aralık dahilinde tarafsız bir şekilde rastgele tamsayılar almanın etkili bir yolu var mı? (Zaman izin verdiği zaman araştırmaya devam edeceğim, ancak bu arada burada birisinin iyi bir fikri olabileceğini düşündüm!)

Cevaplar Tablosu

  1. En basit (ve dolayısıyla taşınabilir) fikir, yeterince uzun bir rasgele bitstring üretmektir. Bir bash yerleşik $RANDOMdeğişkeni kullanarak veya odve /dev/urandom(veya /dev/random) kullanarak rastgele bir bit kuşağı oluşturmanın farklı yolları vardır . Rastgele sayı büyükse $MAX, baştan başlayın.

  2. Alternatif olarak, harici aletler kullanmak da mümkündür.

    • Perl çözümü
      • Pro: Oldukça taşınabilir, basit, esnek
      • Kontra: 2 32 -1 üzerindeki çok büyük sayılar için değil
    • Python çözümü
      • Pro: basit, esnek, büyük sayılar için bile çalışır
      • Kontra: daha az taşınabilir
    • Zsh çözümü
      • Pro: Yine de zsh kullanan insanlar için iyi
      • Kontra: muhtemelen daha az taşınabilir

Neden ras64 bitleri kodlamak yerine base64 kodlamak yerine sadece tamsayıları seçip daha sonra belirli sayıda karakteri (gerekli aralığa bağlı olarak) kodlanmış formdan base10'dan base10'a dönüştürmeliyim?
muru

Bash olması gerekiyor mu ? Gereksinimlerinizi yerine getiren bir tekrarlayıcı döndürürse, böyle bir rand=$(command)şey olur commandmu?
terdon

@muru Bu gerçekten güzel bir fikir. Benzer bir fikir üzerinde bazı düşüncelerimi harcadım, bunu kullanarak dd if=/dev/urandom 2>/dev/nullve bunları kullanarak borulara geçirdim od -t d(tabandan 64 yoldan kaçınır), ancak dönüşümün nasıl gerçekleştiği ve gerçekten tarafsız olup olmadığı bana belli değil. Fikrinizi verimli, çalışan bir senaryoya genişletebilir ve neden önyargı olmadığını açıklayabilirseniz, bu harika bir cevap verecektir. :)
Malte Skoruppa 24:14

@ terdon bash tercih ederim. Tabii ki çağırabilir ait Yani, pythonya perlfavori dili ya, ama bu her yerde mevcut değildir. Daha taşınabilir bir şey tercih ederim. Eh, awkrastgele işlevi iyi olurdu, sanırım. Ama ne kadar taşınabilir, o kadar iyi :)
Malte Skoruppa 24:14

2
Evet, çizgileri boyunca düşünüyordum perl -e 'print int(rand(2**32-1))');. Bu oldukça lanet taşınabilir ve çok hızlı olacak. Çoğu uygulama aynı tohumdan başladığından dolayı Awk bunu kesmeyecek. Böylece sonraki çalışmalarda aynı rasgele sayı elde edersiniz. Sadece aynı çalışma içinde değişir.
terdon

Yanıtlar:


17

Buradan başka ilginç bir yöntem görüyorum .

rand=$(openssl rand 4 | od -DAn)

Bu da iyi bir seçenek gibi görünüyor. Rastgele aygıttan 4 bayt okur ve bunları 0ve arasında işaretsiz tamsayı olarak biçimlendirir 2^32-1.

rand=$(od -N 4 -t uL -An /dev/urandom | tr -d " ")

7
Eğer gereken kullanmak /dev/urandomsize gereken bilmedikçe/dev/random ; /dev/randomLinux'ta engeller.
jfs

odkomutlar neden farklı? Her ikisi de yalnızca 4 baytlık işaretsiz tamsayıları yazdırır: 1 - openssl'den, 2 - den /dev/random.
jfs

1
@Ramesh /dev/urandomyerine kullanmak üzere düzenleme yaptım - kullanmak /dev/randomiçin hiçbir neden göremiyorum /dev/randomve gerçekten pahalı / yavaş olabilir veya sistemin diğer bölümlerini yavaşlatabilir. (Geri düzenleme yapmaktan çekinmeyin ve gerçekten gerekli olup olmadığını açıklayın.)
Volker Siegel

1
Endişeye gerek yok, bu basit farkın çok karmaşık etkileri olması gerçekten şaşırtıcı. Bu yüzden örneği doğru olana çevirmek için ısrar ettim - insanlar örneklerden öğreniyorlar.
Volker Siegel

1
@MalteSkoruppa: bunun prensipte olduğundan daha az olabileceği Ianlamına gelir . btw, başarısız olur ancak çalışmaya devam eder. sizeof(int)4od -DAn(2**32-1)od -N4 -tu4 -An
jfs

8

Tüm güzel cevaplarınız için hepinize teşekkür ederim. Paylaşmak istediğim şu çözümü buldum.

Neden ve nedenlerle ilgili daha fazla ayrıntıya girmeden önce, işte tl; dr : benim parlak yeni senaryomuz :-)

#!/usr/bin/env bash
#
# Generates a random integer in a given range

# computes the ceiling of log2
# i.e., for parameter x returns the lowest integer l such that 2**l >= x
log2() {
  local x=$1 n=1 l=0
  while (( x>n && n>0 ))
  do
    let n*=2 l++
  done
  echo $l
}

# uses $RANDOM to generate an n-bit random bitstring uniformly at random
#  (if we assume $RANDOM is uniformly distributed)
# takes the length n of the bitstring as parameter, n can be up to 60 bits
get_n_rand_bits() {
  local n=$1 rnd=$RANDOM rnd_bitlen=15
  while (( rnd_bitlen < n ))
  do
    rnd=$(( rnd<<15|$RANDOM ))
    let rnd_bitlen+=15
  done
  echo $(( rnd>>(rnd_bitlen-n) ))
}

# alternative implementation of get_n_rand_bits:
# uses /dev/urandom to generate an n-bit random bitstring uniformly at random
#  (if we assume /dev/urandom is uniformly distributed)
# takes the length n of the bitstring as parameter, n can be up to 56 bits
get_n_rand_bits_alt() {
  local n=$1
  local nb_bytes=$(( (n+7)/8 ))
  local rnd=$(od --read-bytes=$nb_bytes --address-radix=n --format=uL /dev/urandom | tr --delete " ")
  echo $(( rnd>>(nb_bytes*8-n) ))
}

# for parameter max, generates an integer in the range {0..max} uniformly at random
# max can be an arbitrary integer, needs not be a power of 2
rand() {
  local rnd max=$1
  # get number of bits needed to represent $max
  local bitlen=$(log2 $((max+1)))
  while
    # could use get_n_rand_bits_alt instead if /dev/urandom is preferred over $RANDOM
    rnd=$(get_n_rand_bits $bitlen)
    (( rnd > max ))
  do :
  done
  echo $rnd
}

# MAIN SCRIPT

# check number of parameters
if (( $# != 1 && $# != 2 ))
then
  cat <<EOF 1>&2
Usage: $(basename $0) [min] max

Returns an integer distributed uniformly at random in the range {min..max}
min defaults to 0
(max - min) can be up to 2**60-1  
EOF
  exit 1
fi

# If we have one parameter, set min to 0 and max to $1
# If we have two parameters, set min to $1 and max to $2
max=0
while (( $# > 0 ))
do
  min=$max
  max=$1
  shift
done

# ensure that min <= max
if (( min > max ))
then
  echo "$(basename $0): error: min is greater than max" 1>&2
  exit 1
fi

# need absolute value of diff since min (and also max) may be negative
diff=$((max-min)) && diff=${diff#-}

echo $(( $(rand $diff) + min ))

Bunu ~/bin/randsaklayın ve uygun olduğunuzda, belirli bir aralıktaki bir tamsayıyı örnekleyebilen bash'ta rastgele bir işlev görürsünüz. Aralık, negatif ve pozitif tamsayılar içerebilir ve uzunlukları 2 60 -1'e kadar olabilir :

$ rand 
Usage: rand [min] max

Returns an integer distributed uniformly at random in the range {min..max}
min defaults to 0
(max - min) can be up to 2**60-1  
$ rand 1 10
9
$ rand -43543 -124
-15757
$ rand -3 3
1
$ for i in {0..9}; do rand $((2**60-1)); done
777148045699177620
456074454250332606
95080022501817128
993412753202315192
527158971491831964
336543936737015986
1034537273675883580
127413814010621078
758532158881427336
924637728863691573

Diğer cevaplayıcıların fikirleri harikaydı. Terdon , JF Sebastian ve jimmij tarafından verilen cevaplar , görevi basit ve verimli bir şekilde yapmak için dış araçları kullandı. Ancak, maksimum taşınabilirlik için gerçek bir bash çözümü tercih ettim ve belki birazcık bash aşkına;

Ramesh 've l0b0 ' ın cevaplarını kullanılmış /dev/urandomveya /dev/randombirlikte od. Ancak bu onların yaklaşımları, bazı n'ler için yalnızca 0 - 2 8n -1 aralığında rasgele tamsayıları örnekleyememenin dezavantajı vardı; çünkü bu yöntem, baytları, yani 8 uzunluk bit bitlerini örneklemektedir. artan

Son olarak, Falco'nun cevabı, keyfi aralıklar için bunun nasıl yapılabileceği ile ilgili genel fikri açıklar (sadece ikisinin güçleri değil). Temel olarak, belirli bir aralık için {0..max}, ikisinin bir sonraki gücünün ne olduğunu, yani bir bitmax kuşağı olarak temsil etmek için tam olarak kaç bitin gerekli olduğunu belirleyebiliriz . Sonra sadece bu kadar çok bit örnekleyebilir ve bu ısırmanın bir tamsayı olarak büyük olup olmadığını görebiliriz max. Eğer öyleyse, tekrarla. Temsil etmek için gereken kadar bit örneklediğimiz için max, her bir yinelemenin başarılı olma% 50'sine eşit veya daha büyük bir olasılığı vardır (en kötü durumda% 50, en iyi durumda% 100). Yani bu çok verimli.

Senaryom temelde, Falco'nun cevabının somut bir uygulamasıdır, istenen baslıktaki bit hatlarını örneklemek için bash yerleşik bitli işlemleri kullanır. Ayrıca Eliah Kağan tarafından , yerleşik $RANDOMdeğişkenin tekrarlanan çağrılardan kaynaklanan bit kızaklarını birleştirerek kullanılmasını öneren bir fikri onurlandırır $RANDOM. Aslında her ikisini de kullanma /dev/urandomve uygulama olanaklarını uyguladım $RANDOM. Varsayılan olarak yukarıdaki komut dosyası kullanır $RANDOM. (Tamam, eğer kullanıyorsanız od ve tr'ye/dev/urandom ihtiyacımız var , ancak bunlar POSIX tarafından destekleniyor.)

Peki nasıl çalışıyor?

Buna girmeden önce, iki gözlem:

  1. Görünüşe göre bash, 2 63 -1'den büyük tam sayıları kaldıramadı . Kendin için gör:

    $ echo $((2**63-1))
    9223372036854775807
    $ echo $((2**63))
    -9223372036854775808

    Bash'ın dahili olarak, tamsayıları depolamak için işaretli 64-bit tam sayıları kullandığı anlaşılıyor. Bu yüzden, 2 63'te "tamamlandı" ve negatif bir tamsayı elde ediyoruz. Bu nedenle, kullandığımız rastgele işlevle, 2 63 -1'den daha büyük bir aralık elde etmeyi umamayız . Bash basitçe halledemez.

  2. Ne zaman minve maxolasılıkla isteğe bağlı bir aralıkta min != 0bir değeri örneklemek istersek, aralarında 0ve max-minyerine bir değeri örnekleyebiliriz ve sonra minnihai sonucu ekleyebiliriz . Bu da çalışır minve muhtemelen de maxvardır negatif ama biz arasında bir değer örneklemek için dikkatli olmak gerekir 0ve mutlak değerini max-min . Öyleyse, rasgele bir değer ile 0isteğe bağlı bir pozitif tamsayı arasında nasıl örnekleme yapılacağına odaklanabiliriz max. Gerisi kolaydır.

Adım 1: Bir tamsayıyı (logaritma) temsil etmek için kaç bitin gerekli olduğunu belirleyin.

Dolayısıyla, belirli bir değer için max, onu bir bit çarpması olarak temsil etmek için kaç bitin gerekli olduğunu bilmek istiyoruz. Bu, daha sonra, sadece gerektiği kadar çok bit rastgele örnekleme yapabilir, bu da betiği çok verimli kılar.

Bakalım. nBitlerle olduğu için, 2 n -1 değerine kadar temsil edebiliriz , daha sonra nkeyfi bir değeri temsil etmek için gereken bit sayısı xtavandır (log 2 (x + 1)). Bu yüzden, logaritmanın tavanını tabana 2 hesaplamak için bir işleve ihtiyacımız var.

log2() {
  local x=$1 n=1 l=0
  while (( x>n && n>0 ))
  do
    let n*=2 l++
  done
  echo $l
}

Bu duruma ihtiyacımız var, n>0eğer çok büyürse, etrafına sarılır ve negatif hale gelirse, döngünün sona ermesi garanti edilir.

Adım 2: Rastgele bir uzunluk n

En taşınabilir fikirler ya kullanmak /dev/urandom(ya da /dev/randomgüçlü bir sebep olsa bile ) ya da bash'ın yerleşik $RANDOMdeğişkenini kullanmaktır. $RANDOMİlk önce nasıl yapılacağına bakalım .

Seçenek A: Kullanma $RANDOM

Bu, Eliah Kagan tarafından belirtilen fikri kullanıyor . Temel olarak, $RANDOM15 bitlik bir tamsayı $((RANDOM<<15|RANDOM))örneklendiğinden, 30 bitlik bir tamsayıyı örneklemek için kullanabiliriz . Bunun anlamı, bir ilk başlatmayı $RANDOM15 bit sola kaydırmak ve bitsel olarak veya ikinci bir $RANDOMbağımsız başlatmayı uygulayarak , iki bağımsız olarak örneklenen bit bağlantı dizisini etkin bir şekilde birleştirmek (ya da en azından bash'ın yerleşik olduğu kadar bağımsız $RANDOM) demektir.

Bunu 45 bit veya 60 bit tam sayı elde etmek için tekrarlayabiliriz. Bundan sonra bash artık üstesinden gelemez, ancak bu kolayca 0 ile 2 60 -1 arasındaki rastgele bir değeri kolayca örnekleyebileceğimiz anlamına gelir . Bu nedenle n-bit bir tamsayıyı örneklemek için, uzunluğunu 15-bit adımda büyüyen rastgele bit-sarımımız n'ye eşit veya daha uzun olana kadar işlemi tekrar ederiz. Sonunda, uygun şekilde bitsel sağa kaydırarak çok fazla olan bitleri keseriz ve sonunda bir n-bit rasgele tamsayı ile sonuçlanır.

get_n_rand_bits() {
  local n=$1 rnd=$RANDOM rnd_bitlen=15
  while (( rnd_bitlen < n ))
  do
    rnd=$(( rnd<<15|$RANDOM ))
    let rnd_bitlen+=15
  done
  echo $(( rnd>>(rnd_bitlen-n) ))
}

Seçenek B: Kullanma /dev/urandom

Alternatif olarak, n-bit bir tamsayıyı kullanabilir odve /dev/urandomörnekleyebiliriz. odbaytları, yani uzunluk bit bitlerini okuyacaktır. Önceki yöntemde olduğu gibi, eşdeğer örneklenmiş bitlerin sayısının n'den büyük veya ona eşit olduğu kadar çok bayt örnekleriz ve çok fazla olan bitleri keseriz.

En az n bit elde etmek için gereken en düşük bayt sayısı n'den büyük veya eşit olan 8 katın en küçük katıdır, yani kat ((n + 7) / 8).

Bu yalnızca 56 bit tam sayıya çalışır. Bir bayt daha fazla örnekleme bize 64-bit bir tamsayı, yani bash'ın kaldıramayacağı 2 64 -1 değerine ulaşır.

get_n_rand_bits_alt() {
  local n=$1
  local nb_bytes=$(( (n+7)/8 ))
  local rnd=$(od --read-bytes=$nb_bytes --address-radix=n --format=uL /dev/urandom | tr --delete " ")
  echo $(( rnd>>(nb_bytes*8-n) ))
}

Parçaları bir araya getirmek: Rasgele tamsayıları rastgele aralıklarla alın

Biz tadabilirsiniz nşimdi bit katarı -bit, ama biz bir aralıkta örnek tamsayılar istediğiniz 0için max, tekdüze rasgele , nerede max, keyfi olabilir ikisinin ille bir güç. (Bu bir önyargı yaratan modulo kullanamayız.)

Değeri temsil etmek için gerektiği kadar çok bit örneklemek için çok uğraştığımız maxnokta, şimdi daha güvenli bir (ve verimli bir şekilde) bir nbit bitstring'i daha düşük bir değer örnekleyene kadar tekrar tekrar örneklemek için bir döngü kullanabiliriz. veya eşittir max. En kötü durumda ( maxikinin gücü), her yineleme% 50 olasılıkla sona erer ve en iyi durumda ( maxiki eksi olanın gücüdür) ilk yineleme kesin olarak sona erer.

rand() {
  local rnd max=$1
  # get number of bits needed to represent $max
  local bitlen=$(log2 $((max+1)))
  while
    # could use get_n_rand_bits_alt instead if /dev/urandom is preferred over $RANDOM
    rnd=$(get_n_rand_bits $bitlen)
    (( rnd > max ))
  do :
  done
  echo $rnd
}

Eşyaları sarmak

Son olarak, minve arasında, ve maxnerede minve maxkeyfi, hatta negatif olabilir tamsayıları örneklemek istiyoruz . Daha önce belirtildiği gibi, bu artık önemsiz.

Hepsini bash betiğine koyalım. Bazı argüman ayrıştırma şeyleri yapın ... İki argüman minve varsayılanların olduğu yerde maxyalnızca bir argüman istiyoruz .maxmin0

# check number of parameters
if (( $# != 1 && $# != 2 ))
then
  cat <<EOF 1>&2
Usage: $(basename $0) [min] max

Returns an integer distributed uniformly at random in the range {min..max}
min defaults to 0
(max - min) can be up to 2**60-1  
EOF
  exit 1
fi

# If we have one parameter, set min to 0 and max to $1
# If we have two parameters, set min to $1 and max to $2
max=0
while (( $# > 0 ))
do
  min=$max
  max=$1
  shift
done

# ensure that min <= max
if (( min > max ))
then
  echo "$(basename $0): error: min is greater than max" 1>&2
  exit 1
fi

... ve son olarak, arasında rasgele bir değerde eşit örnek minve maxbiz arasında rasgele bir sayı örnek 0ve mutlak değerini max-minve ek minnihai sonuca. :-)

diff=$((max-min)) && diff=${diff#-}

echo $(( $(rand $diff) + min ))

Bundan ilham alarak, bu PRNG'yi test etmek ve kıyaslamak için kalıp sökücü kullanmaya çalışabilir ve bulgularımı buraya koyabilirim. :-)


çözümünüz bunun sizeof(int) == 8(64bit) nedeniyle--format=u
jfs

1
Çözümünüz bana random.py'nin nasıl yazıldığını hatırlatıyor. random.Randomsınıf 53bit kullanıyor mu? Jeneratör Rastgele büyük bir rasgele sayılar (çoklu çağrıların) geri dönmek için, random.SystemRandomkullanılarak aynı yapar os.urandom()kullanılarak uygulanabilir edilebilir /dev/urandom.
jfs

uL, aralık için sizeof (uzun)> = 8 değerini belirtir. Garantili değil. U8'i platformun böyle bir tamsayıya sahip olduğunu iddia etmek için kullanabilirsiniz.
jfs

@ JFSebastian Şimdiye kadar senaryomun uzun bir int boyutuyla ilgili herhangi bir varsayımı kodlamadığını düşünüyordum. Potansiyel olarak, uzun imzalı bir int'nin boyutu 64 bitten (örneğin 128 bit) daha büyük (veya daha düşük) olsa bile işe yarar. Ancak, eğer kullanırsam --format=u8, varsayımı sabitlerim sizeof(int)==8. Öte yandan, kullanım eğer --format=uLbir platform vardır sanmıyorum: hayır problem var sahiptir 64 bitlik tamsayılar ama hala düşük bir şey olarak uzun int'leri tanımlar. Bu yüzden temelde --format=uLdaha fazla esnekliğe izin verdiğini iddia ediyorum . Düşüncelerin nelerdir?
Malte Skoruppa

orada long longo olabilir 64bit bazı platformlarda 32 bit = ederken int = uzunluğundadır. Tüm platformlarda garanti edemezseniz, 0..2 ** 60 aralığından bahsetmemelisiniz. Öte yandan, bash bu aralığın kendisini bu tür platformlarda desteklemeyebilir (bilmiyorum, belki maxint_t kullanır ve sonra sabit aralığı odbelirtmek istiyorsanız u8 daha doğrudur ( sizinkinin uygun olup olmadığını belirtmeyi desteklemiyorsa). bash platformuna bağlı olarak ne olursa olsun?) Bash menzili uzunluğuna bağlıysa uL daha uygun olabilir). Bash'ın tüm işletim sistemlerinde desteklediği tüm aralığı mı yoksa sabit bir aralık mı istiyorsunuz?
jfs

6

Zsh olabilir mi?

max=1000
integer rnd=$(( $(( rand48() )) * $max ))

Sen de tohum kullanmak isteyebilirsin rand48(seed). İlgileniyorsanız ayrıntılı açıklama için bkz. man zshmodulesVe man 3 erand48.


Şahsen zsh kullanmıyorum, ama bu harika bir ektir :)
Malte Skoruppa 24:14


5

0 ile (2 ^ n) -1 arasında bir sayı istiyorsanız, burada n mod 8 = 0 'dan basitçe n / 8 bayt alabilirsiniz /dev/random. Örneğin, rastgele bir ondalık gösterimi almak için intşunları yapabilirsiniz:

od --read-bytes=4 --address-radix=n --format=u4 /dev/random | awk '{print $1}'

Sadece n bit almak istiyorsanız, önce tavan (n / 8) bayt ve istediğiniz miktara sağa kaydırma yapabilirsiniz . Örneğin, 15 bit istiyorsanız:

echo $(($(od --read-bytes=2 --address-radix=n --format=u4 /dev/random | awk '{print $1}') >> 1))

Eğer rastgeleliğin kalitesini önemsemediğinizden eminseniz ve bunun yerine kullanabileceğiniz minimal bir çalışma süresi garanti etmek istiyorsanız . Kullanmadan önce ne yaptığınızı bildiğinizden emin olun !/dev/urandom/dev/random/dev/urandom


Teşekkür ederim. Yani, nrastgele bayt almak /dev/urandomve kullanarak biçimlendirin od. Bu cevap olarak ruhu benzer . Her ikisi de eşit derecede iyi :) Her ikisi de sabit bir 0 - 2 ^ (n * 8) -1 bit aralığına sahip olma dezavantajına sahip olsa da, n, bayt sayısıdır. 2 ^ 32-1'e kadar rastgele bir aralık için bir yöntemi tercih ederim , fakat aynı zamanda daha düşük bir şey. Bu önyargı zorluğunu yaratır.
Malte Skoruppa 24:14

Kullanmak /dev/urandomyerine düzenlenmiş - Kullanmak için /dev/randomhiçbir neden göremiyorum /dev/randomve sistemin diğer bölümlerini gerçekten pahalı / yavaş ya da yavaşlatabilir. (Düzenleme yapmaktan çekinmeyin ve gerçekten gerekli olup olmadığını açıklayın.)
Volker Siegel

Tam tersi olmalı: / dev / random'a ihtiyacınız olduğunu bilmiyorsanız / dev / urandom kullanın . /dev/urandomSonuçların, /dev/randomurandomun çoğu durumda kullanılamayacağından daha kötü olduğunu varsaymak yanlıştır . Bir kez /dev/urandombaşlatıldı (sistemin başında); sonuçları /dev/randomLinux'taki hemen hemen tüm uygulamalar için iyidir . Bazı sistemlerde rastgele ve urandom aynıdır.
jfs

1
--format=uile değiştirilmelidir --format=u4çünkü teoride sizeof(int)olduğundan daha az olabilir 4.
jfs

@ JFSebastian Bu yazının konuyla ilgili çok ilginç bir tartışması var. Vardıkları sonuç hem o gibi görünüyor /dev/randomve /dev/urandomtatmin edici olduğunu ve "Linux bunun kadar bloklar yeterli tohum entropi toplanmış ve bundan sonra gibi davranır ki güvenli bir RNG eklemek gerekir urandom."
l0b0

3

Dış araçları kullanmaya itiraz etmediğinizi varsayarsak, bu gereksinimlerinizi yerine getirmelidir:

rand=$(perl -e 'print int(rand(2**32-1))'); 

randParametre olarak üst limiti alan perl işlevini kullanıyor . Ne istersen ayarlayabilirsin. Bunun soyut matematiksel tanımdaki gerçek rastlantısallığa ne kadar yakın olduğu, bu sitenin kapsamı dışındadır, ancak son derece hassas şifreleme veya benzeri için gerekmedikçe iyi olmalıdır. Belki orada bile olsa, bir fikre kapılmayacağım.


bu büyük sayılar için
kesilir

1
@ JFSebastian evet öyle. OP belirtildiğinden beri bunu gönderdim, 1^32-1ancak daha büyük sayılar için ince ayar yapmanız gerekir.
terdon

2

En yakın (2 ^ X) -1 eşit veya istediğiniz maksimum rende almalı ve bit sayısını almalısınız. Ardından, sadece / dev / random komutunu birçok kez arayın ve tüm bitleri, yeterli olana kadar bir araya getirin, çok fazla olan tüm bitleri kısaltın. Elde edilen sayı, maksimum tekrarlamanızdan büyükse. En kötü durumda, Maksimum değerinizin altında rastgele bir sayı elde etme şansınız% 50'den fazladır, bu nedenle (bu en kötü durum için) ortalama iki çağrı alırsınız.


Bu aslında verimliliği arttırmak için oldukça iyi bir fikir. Ramesh'in cevabı ve l0b0'ın cevabı hem temelde rastgele bitler alır /dev/urandom, hem de her iki cevabında da her zaman 8 bittir. Ondalık biçimlendirmeden önce alt aralıklar için çok fazla olan bitlerin kesilmesi, odverimi iyileştirmek için iyi bir fikirdir, çünkü döngü güzel bir şekilde açıkladığınız gibi yalnızca 2 yinelemeden beklenen bir sayıya sahiptir. Bu, söz konusu cevaplardan biriyle birleştiğinde, muhtemelen gidilecek yoldur.
Malte Skoruppa 24:14

0

Cevabınız ilginç ama oldukça uzun.

İsteğe bağlı olarak büyük sayılar istiyorsanız, bir yardımcıya birden çok rastgele sayının katılabilirsiniz:

# $1 - number of 'digits' of size base
function random_helper()
{
  base=32768
  random=0
  for((i=0; i<$1; ++i)); do
    let "random+=$RANDOM*($base**$i)"
  done
  echo $random
}

Sorun önyargı ise, hemen kaldırın.

# $1 - min value wanted
# $2 - max value wanted
function random()
{
  MAX=32767
  min=$1
  max=$(($2+1))
  size=$((max-min))
  bias_range=$((MAX/size))
  while
    random=$RANDOM
  [ $((random/size)) -eq $bias_range ]; do :; done
  echo $((random%size+min))
}

Bu fonksiyonlara birlikte katılmak

# $1 - min value wanted
# $2 - max value wanted
# $3 - number of 'digits' of size base
function random()
{
  base=32768
  MAX=$((base**$3-1))
  min=$1
  max=$(($2+1))
  size=$((max-min))
  bias_range=$((MAX/size))
  while
    random=$(random_helper)
  [ $((random/size)) -eq $bias_range ]; do :; done
  echo $((random%size+min))
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.