Générer un flux binaire pseudo-aléatoire (complètement déterministe)

11

Inspiré par Random avec les mains liées :


Le but

Le but de ce défi est d'écrire un programme qui génère un flux binaire pseudo-aléatoire, qui est une chaîne de 1 et de 0 qui semble être purement aléatoire, mais qui est en fait généré de manière déterministe. Votre programme doit générer une chaîne de 1 et 0 (avec des espaces en option) et doit répondre aux exigences suivantes:

  1. Étant donné le temps et la mémoire illimités, votre programme doit continuer à produire une chaîne de 1 et de 0 pour toujours
  2. Votre programme doit produire plus de 1000 bits aléatoires en une minute environ, sur une machine raisonnable. Si cette exigence est impossible, je la diminuerai.
  3. La chaîne de bits peut se répéter, mais la longueur de la section extensible doit être supérieure à 1 000 bits.
  4. La chaîne de bits doit réussir autant de tests d'aléatoire (décrits ci-dessous) que possible.
  5. Le programme ne doit prendre aucune entrée d'aucune source externe ni utiliser aucune fonction de type rand () intégrée.
  6. En raison de l'exigence ci-dessus, le programme doit générer la même chaîne de bits exacte à chaque exécution.

Test de hasard # 1

La chaîne de bits pseudo-aléatoires ne doit pas inclure de motif évident lors de l'inspection visuelle.

Test de hasard # 2 (sujet à changement en fonction des commentaires)

La chaîne de bits doit contenir une distribution égale de 1 et de 0. Pour tester cela (et d'autres choses aussi), le flux de bits est divisé en segments de 3 bits, tels que 101|111|001.

De tous ces segments, 1/8 d'entre eux devraient avoir trois 1 et aucun 0, 3/8 d'entre eux devraient avoir deux 1 et un 0, 3/8 d'entre eux devraient avoir un 1 et deux 0 et 1/8 d'entre eux ne devraient pas avoir de 1 et trois 0.

Test de hasard # 3

Une «exécution» est définie comme une série consécutive de bits qui ont tous la même valeur. La chaîne 1001001110a trois exécutions de taille 1 ( 1..1.....0), deux exécutions de taille 2 ( .00.00....) et une exécution de taille 3 ( ......111.). Notez que les exécutions ne se chevauchent pas.

Sur une chaîne de 1000 bits aléatoires, il devrait y avoir environ 250 exécutions de taille 1, 125 exécutions de taille 2, 62 exécutions de taille 3, etc. En général, pour la taille d'exécution R, il devrait y avoir environ des 1000/(2**(R+1))exécutions de cette taille.

Test de hasard # 4

Les 840 premiers bits sont divisés en deux moitiés de 420 bits chacune. Chaque bit de la première moitié est comparé au bit correspondant de la seconde moitié. Les deux bits doivent correspondre à environ cinquante pour cent du temps.


Voici le code source d'un programme Perl qui effectue les tests 2 à 4. Pour l'instant, il requiert que la chaîne de bits ne contienne aucun espace.


Temps de critère de gain objectif!

Le gagnant est le programme qui réussit toutes les 6 exigences et tous les tests de hasard dans la mesure où il est impossible de le distinguer du hasard. Si plusieurs programmes accomplissent cela, alors celui qui prend le plus de temps à répéter gagnera. Si plusieurs programmes accomplissent cela, je devrai peut-être trouver plus de tests d'aléatoire pour agir en tant que bris d'égalité.

PhiNotPi
la source
Les # 2 et # 3 ne sont pas vraiment de très bons critères de hasard. Pour le # 2 en particulier, un échantillon aléatoire ne présentera probablement pas cette caractéristique. Vous pouvez peut-être faire un échantillon plus grand? Je suggérerais quelque chose entre 100 et 300.
Joel Cornett
Une meilleure méthode de mesure serait une moyenne mobile, car la moyenne sur une grande fenêtre sur le flux binaire ne changera pas beaucoup (et devrait être d'environ 0,5)
Joel Cornett
@JoelCornett Merci pour les conseils. Je ne connais pas grand-chose aux tests de hasard. Je vais changer # 2 pour autre chose, et je lis sur les moyennes mobiles.
PhiNotPi
1
Aucun problème. Les séquences aléatoires ont tendance à s'agglutiner et à ne pas être uniformément réparties, c'est un fait qui est parfois utilisé en comptabilité pour détecter les fraudes. (Les nombres frauduleux seront souvent distribués de manière trop égale, car les personnes qui les inventent confondent uniformité et caractère aléatoire)
Joel Cornett
Puis-je utiliser des fonctions de cryptage intégrées (telles que AES ou SHA-2)?
CodesInChaos

Réponses:

8

C, 61

main(s,n){for(n=1u<<31;putchar((s%=n)/(n/2)&1|48);s*=65539);}

Ouais, je sais que ce n'est pas du golf de code. C'est évidemment plutôt une anti-solution ... mais elle remplit bien sûr vos critères.

out | tête -c840
$ ./a.out | tête -c840 | perl tester.pl
Test 2: 1 (1) 2.93333333333333 (3) 3.1 (3) 0.966666666666667 (1)
Test 3: 214 99 71 24 7 5 1 1 2 2
Test 4: 0.495238095238095

La durée de la période est de 2²⁹.

a cessé de tourner dans le sens antihoraire
la source
6
Cela montre à quel point il est difficile de distinguer le caractère aléatoire de quelque chose qui est largement connu pour être l'un des pires générateurs de nombres aléatoires existants. +1.
PhiNotPi
8

Mathematica 78 53 caractères

Les chiffres de la représentation binaire de Pi semblent se comporter comme s'ils étaient produits de manière chaotique, bien que cela ne soit pas prouvé.

La routine simple suivante retourne de manière déterministe sous forme de chaîne les chiffres binaires de pi, correspondant aux dchiffres décimaux:

f[d_]:=ToString@FromDigits@RealDigits[N[Pi,d],2][[1]]

Usage

Si nous demandons l'équivalent de 301 chiffres décimaux de Pi, nous recevons 1000 chiffres binaires.

f[301]
StringLength[%]

(* out *)
1100100100001111110110101010001000100001011010001100001000110100110001001100011001100010100010111000000011011100000111001101000100101001000000100100111000001000100010100110011111001100011101000000001000001011101111101010011000111011000100111001101100100010010100010100101000001000011110011000111000110100000001001101110111101111100101010001100110110011110011010011101001000011000110110011000000101011000010100110110111110010010111110001010000110111010011111110000100110101011011010110110101010001110000100100010111100100100001011011010101110110011000100101111001111110110001101111010001001100010000101110100110100110001101111110110101101011000010111111111101011100101101101111010000000110101101111110110111101110001110000110101111111011010110101000100110011111101001011010111010011111001001000001000101111100010010110001111111100110010010010010100001100110010100011110110011100100010110110011110111000010000000000111110010111000101000010110001110111111000001011001100011011010010010000011011000011100011

1000 (* characters *)

Parce que Pi est un nombre irrationnel, il n'y a pas de point. Cependant, il y aura des contraintes pratiques en raison du matériel utilisé.

Test 1 me semble bien.

Test 2

d=301;
Partition[RealDigits[N[Pi,d],2][[1]],{3}];
Tally[%]
(* out *)
{{{1,1,0},35},{{0,1,0},45},{{0,0,0},41},{{1,1,1},40},
{{0,1,1},50},{{1,0,1},32},{{1,0,0},43},{{0,0,1},47}}

Vérification plus approfondie:

d=10^6;
Partition[RealDigits[N[Pi,d],2][[1]],{3}];
Tally[%]

{{{1,1,0},138565},{{0,1,0},138146},{{0,0,0},138260},{{1,1,1},138427},
{{0,1,1},139119}, {{1,0,1},138404},{{1,0,0},137926},{{0,0,1},138462}}

Test 3: exécutions

d=10^6;
res3=SortBy[Tally@Split@RealDigits[N[Pi,d],2][[1]],Last]/.{a_,b_}:> {Length[a],b}
ListPlot[res3 ,AxesLabel-> {"Run Length","Runs"},AxesOrigin->{0,0}]

J'ai exécuté un grand nombre de cas pour vérifier systématiquement la distribution des runs. En environ 3 millions de chiffres binaires, il y a eu 830 000 exécutions de 1, 416 000 exécutions de 2, 208 000 exécutions de 3, 104 000 exécutions de 4, etc.

exécute 2 Test 4: Correspondance de la première et de la seconde moitié des données

Les correspondances sont les 212 cas de 0 et 2; les disparités correspondent aux 208 cas où la somme des chiffres respectifs est 1.

d=301;
Tally[Plus@@Partition[Take[RealDigits[N[Pi,d],2][[1]],840],420]]

(* out *)
{{1,208},{0,108},{2,104}}

Horaire

Il faut moins de deux secondes pour calculer 3321928 chiffres binaires (correspondant à 10 ^ 6 chiffres décimaux).

(r=f[10^6]);//AbsoluteTiming
StringLength[r]

(*out*)
{1.785928,Null}    
3321928
DavidC
la source
1
Je savais que quelqu'un ferait ça ...
cessé de tourner dans
1
Des fruits bas, non?
DavidC
Ne pourriez-vous pas utiliser eau lieu d' pienregistrer un octet?
pppery
Est edistribué de manière chaotique?
DavidC
3

Python, 90

g=[19]
print(''.join("01"[(g.append((11*g[-1]+13)%1024)or g[-1])>512]for i in range(1000)))

gest la valeur de départ. L'échantillonnage aléatoire présente une distribution remarquablement normale. L'échantillonnage aléatoire répété des moyennes de l'échantillon a donné une moyenne 0.506et un écart-type de .0473(taille de l'échantillon de 1 000). Malheureusement, le caractère aléatoire est très sensible à la semence initiale. La graine dans le code ci-dessus m'a donné le meilleur caractère aléatoire: p

MISE À JOUR

Voyons comment ce code résiste aux tests de l'OP:

Test n ° 1

Celui-ci est un peu subjectif ... mais il me semble assez irrégulier.

Test n ° 2

Trois 1: 0,141
Deux 1: 0,371
Un 1: 0,353
Zéro 1: 0,135

Test n ° 3

Fonctionne par taille:

8: 11
7: 3
6: 7
5: 13
4: 32
3: 67
2: 119
1: 216

Test n ° 4

Ratio d'égalités: 0,94 C'est une faute de frappe. Mettra à jour avec le bon numéro bientôt.

Joel Cornett
la source
1
Vous pouvez supprimer l'espace avant «pour».
daniero
2

Haskell 74 58

main=print$iterate(read.take 9.show.(^3))7>>=show.(`mod`2)

Merci à shiona pour la simplification. Résultats:

/ pseudo-aléatoire | tête -c 1000

./pseudorandom | tête -c 1000 | perl test.pl

Test 2: 0,966666666666667 (1) 2,4 (3) 3,3 (3) 1,33333333333333 (1)

Test 3: 260 108 66 33 15 11 5 2

Test 4: 0,495238095238095

C'est aussi un terrible générateur pseudo-aléatoire (similaire à celui utilisé par von-Neuman). Pour ceux qui n'étaient pas au courant concatMap == (=<<) == flip . (>>=)(pour les listes)

walpen
la source
Vous pouvez remplacer \x->if odd x then"1"else"0"par show.(`mod`2).
shiona
1

La question est essentiellement équivalente à "implémenter un chiffrement de flux". J'implémente donc RC4, car c'est relativement simple.

Je n'utilise aucune clé et je laisse tomber les 100 000 premiers bits, car le début de RC4 est un peu biaisé, d'autant plus que j'ai sauté la programmation des clés. Mais je m'attendrais à ce qu'il passe votre test même sans cela (sauvegarde de 20 caractères de code).

Normalement, on produirait un octet complet par cycle, mais la conversion en binaire est plutôt moche en C #, donc je rejette simplement tout sauf le bit le moins significatif.

var s=Enumerable.Range(0,256).ToArray();
byte i=0,j=0;
for(int k=0;;k++)
{
    i++;
    j+=(byte)s[i];
    var t=s[i];s[i]=s[j];s[j]=t;
    if(k>99999)
        Console.Write(s[i]+s[j]&1);
}

Ou sans espaces:

var s=Enumerable.Range(0,256).ToArray();byte i=0,j=0;for(int k=0;;k++){i++;j+=(byte)s[i];var t=s[i];s[i]=s[j];s[j]=t;if(k>99999)Console.Write(s[i]+s[j]&1);}

C #, 156 caractères, fonctionne en mode déclaration de LinqPad. Pour un programme C # complet, ajoutez le passe-partout habituel.


Nous pourrions également utiliser des primitives cryptographiques intégrées (solution de tricheur):

var h=SHA256.Create();for(BigInteger i=0;;i++){Console.Write(h.ComputeHash(i.ToByteArray())[0]%2);}

(C #, 99 caractères, fonctionne en mode instruction de LinqPad. Pour le compilateur C # normal, vous devrez ajouter un peu de passe-partout)

La sortie des fonctions de hachage cryptographiques est conçue pour être impossible à distinguer des données aléatoires, donc je m'attends à ce qu'elle passe tous les tests d'aléatoire (mourir plus fort, ...) que vous lui lancez, mais je suis trop paresseux pour tester.

CodesInChaos
la source
1

C, 52 caractères

main(a){for(a=1;putchar(48+a%2);a=a/2^-(a%2)&576);}

Il s'agit d'un LFSR 10 bits, résultats des tests:

$ ./a.out |head -c 1000 | perl randtest.pl
Test 2: 1.13333333333333 (1) 2.86666666666667 (3) 3.16666666666667 (3) 0.833333333333333 (1)
Test 3:  251 122 64 32 16 8 4 2  1
Test 4: 0.466666666666667
Hasturkun
la source
adevrait commencer comme 1, (en supposant qu'il est appelé sans arguments). Vous pouvez également coller le a=au milieu, quelque chose comme a=a/2^-!putchar(49-a%2)%576(prendre quelques libertés avec l'algorithme)
walpen
@walpen: Mon implémentation initiale n'a pas été définie a, je l'ai changé à cause de " The program must not take any input from any external sources"
Hasturkun
1

Sage / Python

Ce programme imprime les chiffres binaires les plus à droite qui sont communs à chaque tour d'exponentiation suffisamment haute de la forme 3 3 3 3 . . . Pour tout ce qui pourrait jamais être généré, ce sont les chiffres binaires les plus à droite du nombre de Graham . La séquence de chiffres est infinie et n'est pas périodique.

m = 1; x = 3; last = 0
while True:
    m *= 2; x = pow(3,x,m); l = len(bin(x))
    print '1' if l > last else '0',
    last = l

Pour 1000 chiffres, cela a pris moins de 2 secondes; cependant, le temps augmentera beaucoup plus rapidement que linéairement le nombre de chiffres.

Les résultats des tests utilisant le programme OP sont

Test 2: 1.26666666666667 (1) 3.16666666666667 (3) 2.8 (3) 0.766666666666667 (1)
Test 3:  268 126 61 30 20 7 2  1 1
Test 4: 0.466666666666667

(Voir Les chiffres de G les plus à droite sont-ils aléatoires? Pour plus de 32 000 chiffres et des tests statistiques supplémentaires.)

res
la source
1

Java, 371 317

Basé sur un LFSR 128 bits (les taps de bits proviennent de la note 52 de l'application xilinx )

EDIT: Je n'étais pas satisfait d'utiliser BigInteger, donc cette version ne fonctionne pas. Enregistré quelques caractères. La sortie pourrait être un peu moins aléatoire car je ne pouvais pas penser à une bonne méthode de «semis».

Nouveau code: Arguments: BITS_TO_PRINT

class R{public static void main(String[]a){int L=65536;int[]v={0,128,126,101,99};int[]b=new int[L];for(int x=0;x<L;x++)b[x]=(x*x)&1;for(int i=0;i<Integer.parseInt(a[0])+L;i++){if(1!=(b[v[1]]^b[v[2]]^b[v[3]]^b[v[4]]))b[v[0]]=1;else b[v[0]]=0;if(i>L)System.out.print(b[v[0]]);for(int j=0;j<5;j++)v[j]=(v[j]-1)&(L-1);}}}

Ancienne version: Arguments: SEED, BITS_TO_PRINT

import java.math.BigInteger;class R{public static void main(String[]a){BigInteger v=new BigInteger(a[0]);BigInteger m=new BigInteger("ffffffffffffffffffffffffffffffff",16);for(int i=Integer.parseInt(a[1]);i>0;i--){v=v.shiftLeft(1);if(!(v.testBit(128)^v.testBit(126)^v.testBit(101)^v.testBit(99))){v=v.setBit(0);}v=v.and(m);java.lang.System.out.print(v.testBit(0)?1:0);}}}

Nouvelle version: exemple de sortie, bits = 100:

011001100111000110010100100111011100100111000111001111110110001001100000100111111010111001100100011
Noé
la source
1
BTW, je suppose que les deux comptes Noah de ce post sont la même personne. Si c'est le cas, vous pouvez demander à un modérateur de les fusionner sur meta.codegolf.stackexchange.com
Peter Taylor
0

JavaScript - 1 ms à 2 ms pour 1000 bits pseudo-aléatoires (139 ms à 153 ms pour 100 000 bits)

Cette solution utilise le fait que les racines carrées sont irrationnelles et donc assez aléatoires. Fondamentalement, il prend la racine carrée de 2 pour commencer, la convertit en binaire, jette la partie principale qui correspond à la racine précédente, ajoute cela à la chaîne aléatoire, répète avec le nombre supérieur suivant (ou revient à 2 si le nombre répété et avait au moins 30 bits de long), et renvoie la chaîne aléatoire une fois qu'elle est suffisamment longue.

var getDeterministicPseudoRandString = function(length){
    var randString = '';

    var i = 2;
    var prevRand = '';

    outerLoop:
    while(randString.length < length){
        var nextRand, nextFullRand = Math.sqrt(i++).toString(2).substring(1).replace('.', '');
        nextRand = nextFullRand;
        for(var j = prevRand.length; j > 0; j--){
            var replaceString = prevRand.substring(0, j);

            nextRand = nextFullRand;

            if(nextFullRand.indexOf(replaceString) == 0){
                if(j == prevRand.length && j > 30){
                    //start i over at 2
                    console.log('max i reached: ' + i);

                    i = 2;
                    continue outerLoop;
                } else {
                    nextRand = nextFullRand.replace(replaceString, '');
                }

                break;
            }
        }
        prevRand = nextFullRand;

        randString += nextRand;
    }

    return randString.substring(0, length);//Return the substring with the appropriate length
};

Je ne l'ai pas encore parcouru les tests, mais j'imagine que cela leur conviendra. Voici un violon pour que vous puissiez le voir en action. Pour mon temps, j'ai simplement exécuté le programme plusieurs fois et pris les valeurs les plus rapides et les plus lentes comme plages.

Briguy37
la source
0

Python

import hashlib
x=''
while 1:
    h=hashlib.sha512()
    h.update(x)
    x=h.digest()
    print ord(x[0])%2

Devrait avoir une période d'environ 2 ^ 512.

Keith Randall
la source
0

perl, 44 octets

Je sais que ce n'est pas du golf de code, mais j'ai toujours été fan de prendre les bits de poids faible d'une simple fonction quadratique, par exemple:

$x=1/7;print substr($x*=4-4*$x,9,1)%2while 1

La période dépasse 3 milliards, mais je n'ai plus d'espace disque pour en calculer davantage.

skibrianski
la source
1
vous pouvez enregistrer 3 caractères en juxtaposant des constantes numériques et des mots-clés et en les distribuant également 4:$x=1/7;print substr($x*=4-4*$x,9,1)%2while 1
ardnew