Afficher une liste de tous les nombres rationnels

13

De toutes les mathématiques, il y aura toujours quelques théorèmes qui vont au-delà de tout bon sens. L'un d'eux est le fait qu'il existe différentes tailles d'infini. Un autre fait intéressant est l'idée que de nombreux infinis qui semblent être de taille différente sont en fait de même taille. Il y a autant de nombres pairs que d'entiers qu'il y a de nombres rationnels.

Le concept général de cette question est de confronter l'étrange réalité de l'infini. Dans ce défi, votre programme affichera une liste qui:

  • À tout moment spécifique, ayez toujours un nombre entier d'entrées
  • Contenir éventuellement (s'il reste suffisamment longtemps) tout nombre rationnel spécifique (non nul) précisément une fois sur la liste entière
  • Contient un nombre illimité d'emplacements vides (entrées de la liste qui sont inutilement définies à 0)
  • Avoir une proportion d'emplacements vides qui approche une limite de 100%
  • Pour chaque entier positif N, avoir un nombre infini de places avec N emplacements vides consécutifs

Le défi

Votre défi est d'écrire ce programme le plus court possible qui produira une liste spéciale avec les règles suivantes:

  1. Toutes les entrées dont l'index n'est pas un nombre carré doivent être définies sur zéro. Ainsi, la première entrée sera différente de zéro, la deuxième et la troisième seront nulles, la quatrième sera différente de zéro, etc.
  2. Tous les nombres rationnels se présenteront sous la forme d'une fraction impropre (telle que 4/5 ou 144/13) qui a été simplifiée. L'exception est les zéros, qui seront tout simplement 0.
  3. Tous les nombres rationnels (positifs et négatifs) devraient éventuellement apparaître dans la liste si votre programme fonctionne assez longtemps et avec suffisamment de mémoire. Pour tout nombre rationnel particulier, le temps requis peut être un temps arbitrairement grand, mais toujours fini.
  4. S'il est exécuté pendant une durée infinie, aucun nombre rationnel non nul ne doit jamais apparaître deux fois.

La règle 3 permet une certaine variation, car il existe un nombre infini de différentes sorties légales possibles.

La sortie sera un flux de lignes. Chaque ligne sera de la forme générale 5: 2/3où le premier numéro est le numéro d'entrée, suivi du numéro rationnel. Notez que ce 1: 0sera toujours la première ligne de sortie.

Exemple d'extrait de sortie:

1: 1/1
2: 0
3: 0
4: 2/1
5: 0
6: 0
7: 0
8: 0
9: -2/1
10: 0
etc...

Règles, règlements et notes

C'est le golf de code. Les règles de golf à code standard s'appliquent. De plus, en raison de la variation autorisée dans la sortie, vous devez au moins montrer pourquoi vous pensez que votre liste contiendra tous les nombres rationnels possibles une seule fois et que votre solution est correcte.

EDIT: Étant donné que les nombres premiers ont distrait du défi, je le change en nombres carrés. Cela atteint le même objectif et raccourcit également les solutions.

PhiNotPi
la source
1
Quel est l'intérêt de la règle 1? Voulez-vous simplement que les gens jouent mutuellement à deux programmes distincts pour tester la primalité et énumérer les justifications?
Peter Taylor
Il montre comment une très petite partie des entiers a toujours la même cardinalité que l'ensemble complet de nombres rationnels, et il permet également au pourcentage d'emplacements vides d'approcher (mais jamais d'atteindre) 100%.
PhiNotPi
Je suppose que le programme doit également s'exécuter dans une quantité fixe de mémoire, c'est-à-dire qu'il ne peut pas supposer que la machine peut toujours allouer plus? En outre, est-ce contraire aux règles d'utiliser (par exemple) un C int pour l'index de liste lorsque vous savez qu'il a une plage finie? (Même si la limite exacte peut varier avec l'implémentation.) Une forme de bignum est-elle requise?
boîte à pain
1
@PhiNotPi, il existe des moyens beaucoup plus simples de le faire, et c'est une distraction de la partie la plus intéressante de la question.
Peter Taylor
1
Notez que ce 1: 0sera toujours la première ligne de sortie. - Cela contredit votre exemple et n'a pas non plus de sens pour moi.
Wrzlprmft

Réponses:

6

Haskell, 184 caractères

main=putStr.unlines$zip[1..](s>>=g)>>=h
s=(1,1):(s>>=f)
f(a,b)=[(a,a+b),(a+b,b)]
g x@(a,b)=[x,(-a,b)]
h(i,(a,b))=(i^2)%(u a++'/':u b):map(%"0")[i^2+1..i*(i+2)]
i%s=u i++": "++s
u=show

Cela fait une première traversée de la arbre de Calkin-Wilf , donnant exactement une fois tous les nombres rationnels positifs sous forme réduite. Il alterne ensuite entre positif et négatif pour couvrir tous les nombres rationnels non nuls et les tampons avec des zéros entre les entrées carrées.

Sortie (à l'exclusion des lignes nulles par souci de concision):

1: 1/1
4: -1/1
9: 1/2
16: -1/2
25: 2/1
36: -2/1
49: 1/3
64: -1/3
81: 3/2
100: -3/2
...
hammar
la source
5

Sauge, 103 113 128

Sage peut facilement énumérer les justifications! Le formatage pour s'adapter aux exigences du programme, comme toujours, gâche tout.

for i,q in enumerate(QQ):
 for j in[(i-1)^2+1..i*i]:print'%d:'%j,[0,'%d/%d'%(q.numer(),q.denom())][j==i*i]

Sage énumère en QQfonction de leur taille : la valeur absolue maximale du numérateur et du dénominateur après réduction GCD.

boothby
la source
Vous pouvez éliminer x.next()et utiliser une printseule fois, comme suit, ce qui porte le score jusqu'à 124: x=enumerate(QQ) for i,q in x: for j in[(i-1)^2+1..i*i]: print'%d: '%j,'%d/%d'%(q.numer(),q.denom())if j.is_square()else 0. Cela ne s'affiche pas correctement dans un commentaire, mais je pense que vous pouvez voir ce que je veux dire.
res
BTW, je remarque qu'après les 4 premiers éléments positifs, l'énumération de Sage n'est pas la même que dans les autres réponses. Les formules de Calkin-Wilf donnent une séquence dans laquelle le dénominateur d'un rationnel est le numérateur du rationnel suivant; par exemple (..., 1/3, 3/2, 2/3, ...), par rapport à Sage (..., 1/3, 3/1, 2/3, ...). Je n'arrive pas à trouver de documentation pour l'énumération de Sage, pour voir comment elle est calculée.
res
@res, merci! Je voulais fusionner les instructions print, mais j'ai oublié d'utiliser la notation [x..y]. Super de voir un autre utilisateur Sage ici!
stand
4

Python, 162

f=lambda n:f(n/2)if n%2 else f(n/2)+f(n/2-1)if n else 1
n=i=1
while 1:
 print'%d:'%i,
 if i-n*n:s=0
 else: n+=1;s='%d/%d'%((-1)**n*f(n/2-1),f(n/2))
 print s
 i+=1

Cela utilise la récursivité donnée dans Recounting the Rationals par Calkin & Wilf.

res
la source
2

Haskell, 55 octets

mapM_ print$join$iterate(>>=(\x->[x+1,1/(1+1/x)]))[1%1]

les sorties

1 % 1
2 % 1
1 % 2
3 % 1
2 % 3
3 % 2
1 % 3
4 % 1
...

1% 1 est la racine de l'arbre Calkin-Wilf; l'itération ajoute les deux enfants de chaque nœud; la jointure réduit les niveaux en une seule liste.

120 caractères si vous ajoutez les importations appropriées, 0 et les négatifs:

import Data.Ratio
import Control.Monad
main=mapM_ print$0:(join(iterate(>>=(\x->[x+1,1/(1+1/x)]))[1%1])>>=(\x->[-x,x]))

les sorties

0 % 1
(-1) % 1
1 % 1
(-2) % 1
2 % 1
(-1) % 2
1 % 2
(-3) % 1
3 % 1
(-2) % 3
2 % 3
(-3) % 2
3 % 2
(-1) % 3
1 % 3
(-4) % 1
4 % 1
...

sortie des emplacements vides? c'est de mauvais goût :( vous m'avez eu à "la liste de toutes les justifications positives"

John Tromp
la source
mapM_ print$fix((1%1:).(>>= \x->[x+1,1/(x+1)]))est de 47 caractères. de haskellwiki . fonctionne comme il est, sans les importations, à haskell.org est « l' essayer » REPL (bien, sans mapM_ printpartie ...)
Will Ness
1

PHP 105 octets

Remarque: ce code doit être enregistré sous le nom iso-8859-1 (ansi) pour fonctionner correctement. Les interprètes en ligne qui codent toutes les entrées de utf8 par défaut (comme ideone) généreront une sortie incorrecte.

<?for($f=µ;$i++<$j*$j||++$j%2||(--$$f?$$f--:$f^=C);)echo"$i: ",$i==$j*$j?$j%2?$x=++$ö.~Ð.++$µ:"-$x":0,~õ;

Utilisation de l' énumération de Georg Cantor (légèrement modifiée pour les valeurs +/-).

Si vous rencontrez des problèmes pour exécuter le code ci-dessus (probablement en raison d'une quantité excessive de messages NOTICE), utilisez-le à la place (107 octets):

<?for($f=µ;$i++<$j*$j||++$j%2||(--$$f?$$f--:$f^=C);)echo"$i: ",$i==$j*$j?$j%2?$x=++$ö.'/'.++$µ:"-$x":0,'
';
primo
la source
1
Je reçois des erreurs d'exécution avec ce code (qui semble contenir des caractères étranges; par exemple, "$ ö. ~ Ð.").
res
Pouvez-vous démontrer que cette solution fonctionne, disons sur ideone? Je reçois aussi des erreurs: ideone.com/ru1fo
mellamokb
Ideone semble commettre une erreur lorsque trop de messages NOTICE sont générés: ~ Ð (égal à '/') et ~ õ (égal à "\ n") génèrent un AVIS à chaque itération. Bien sûr, si vous avez des AVIS, ce n'est pas un problème. Une pâte avec les deux remplacés (107 octets): ideone.com/lFUbl
primo
Je viens de remarquer que l'interpréteur PHP d'Ideone génère la mauvaise sortie. Si vous exécutez le code localement, vous verrez qu'il est correct. Ou, vous pouvez le tester avec un interpréteur PHP valide, tel que le vérificateur de performances d'Anarchy Golf: golf.shinh.org/checker.html (l'enregistrer dans un fichier et le télécharger)
primo
Lorsque j'enregistre votre code révisé dans un fichier avec un codage ANSI, il s'exécute sur l'interpréteur Anarchy Golf. Cependant, il y a maintenant un problème différent: il viole l'exigence selon laquelle «aucun nombre rationnel non nul ne doit jamais apparaître deux fois» dans la liste. En fait, le code semble répertorier chaque rationnel infiniment de fois; par exemple 1/1, 2/2, 3/3, ... sont tous les mêmes rationnels, et de même pour 1/2, 2/4, 3/6, ..., etc.
res
0

Octave, 168 octets

a=b=p=1;do for i=(p-1)^2+1:p^2-1 printf("%d: 0\n",i)end
printf("%d: %d/%d\n",p^2,a,b)
a=-a;if a>0do if b==1 b=a+1;a=1;else a++;b--;end until 1==gcd(a,b)end
p++;until 0

La solution n'est pas très sophistiquée, c'est juste une simple traversée diagonale du "tapis" des nombres rationnels, en écartant toutes les fractions qui peuvent être simplifiées. Après un nombre positif a/b, son opposé -a/best toujours imprimé avant le prochain de la séquence.

Traversée diagonale de toutes les logiques positives

Étant donné que toutes les fractions simples positives seront imprimées et que les fractions signées opposées à celles-ci seront imprimées, et qu'il n'est jamais possible que deux fractions simples différentes aient la même valeur, chaque nombre rationnel non nul sera imprimé exactement une fois.

Dégolfé:

a=b=p=1
do
    for i=(p-1)^2+1:p^2-1
        printf("%d: 0\n",i)         # p=2,3,4: 1..3,5..8,10..15
    end
    printf("%d: %d/%d\n", p^2,a,b); # p=2,3,4: 4,9,16
    a=-a;
    if a>0                          # the rule is: after a/b, a>0 output -a/b
        do
            if b==1 b=a+1;a=1; else a++;b--; end
        until 1==gcd(a,b)
    end
    p++;
until 0
pawel.boczarski
la source