Saída de uma lista de todos os números racionais

13

Fora de toda a matemática, sempre haverá alguns teoremas que vão além de todo senso comum. Um deles é o fato de que existem diferentes tamanhos de infinito. Outro fato interessante é a idéia de que muitos infinitos que parecem ter tamanhos diferentes são realmente do mesmo tamanho. Existem tantos números pares quanto números inteiros, pois existem números racionais.

O conceito geral desta questão é confrontar a realidade bizarra do infinito. Neste desafio, seu programa exibirá uma lista que:

  • Em qualquer momento específico, sempre tenha um número inteiro de entradas
  • Eventualmente, contenha (se houver tempo suficiente para executar) qualquer número racional específico (diferente de zero) precisamente uma vez na lista inteira
  • Conter um número ilimitado de slots vazios (entradas na lista que são desnecessariamente definidas como 0)
  • Tenha uma proporção de slots vazios que se aproxime de um limite de 100%
  • Para todo número inteiro positivo N, tenha um número infinito de lugares com N espaços vazios consecutivos

O desafio

Seu desafio é escrever o programa mais curto possível que produzirá uma lista especial com as seguintes regras:

  1. Todas as entradas com um índice que não seja um número quadrado devem ser definidas como zero. Portanto, a primeira entrada será diferente de zero, a segunda e a terceira serão zero, a quarta será diferente de zero, etc.
  2. Todos os números racionais terão a forma de uma fração imprópria (como 4/5 ou 144/13) que foi simplificada. A exceção são zeros, que serão simplesmente 0.
  3. Todos os números racionais (positivos e negativos) devem aparecer na lista se o programa for executado por tempo suficiente e com memória suficiente. Para qualquer número racional específico, o tempo necessário pode ser uma quantidade arbitrariamente grande, mas sempre finita.
  4. Se executado por uma quantidade infinita de tempo, nenhum número racional diferente de zero deve aparecer duas vezes.

A regra 3 permite algumas variações, pois há um número infinito de diferentes saídas legais possíveis.

A saída será um fluxo de linhas. Cada linha terá a forma geral de 5: 2/3onde o primeiro número é o número da entrada, seguido pelo número racional. Observe que 1: 0sempre será a primeira linha de saída.

Exemplo de trecho de saída:

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

Regras, regulamentos e notas

Isso é código de golfe. Aplicam-se regras de código padrão de golfe. Além disso, devido à variação permitida na saída, você precisa pelo menos mostrar por que acredita que sua lista conterá todos os números racionais possíveis exatamente uma vez e que sua solução está correta.

EDIT: Como os números primos se distraíram do desafio, estou mudando para números quadrados. Isso cumpre o mesmo objetivo e também reduz as soluções.

PhiNotPi
fonte
1
Qual é o objetivo da regra 1? Você quer que as pessoas joguem dois programas separados para testar a primalidade e enumerar os racionais?
Peter Taylor
Ele mostra como uma parte muito pequena dos números inteiros ainda tem a mesma cardinalidade que o conjunto completo de números racionais e também permite que a porcentagem de slots vazios se aproxime (mas nunca atinja) 100%.
PhiNotPi
Estou assumindo que o programa também precise ser executado em uma quantidade fixa de memória, ou seja, ele não pode assumir que a máquina sempre pode alocar mais? Além disso, é contra as regras usar (digamos) um C int para o índice da lista quando você sabe que ele tem um intervalo finito? (Embora o limite exato possa variar com a implementação.) É necessária alguma forma de bignum?
breadbox
1
@PhiNotPi, existem maneiras muito mais simples de fazer isso, e isso é uma distração da parte mais interessante da pergunta.
Peter Taylor
1
Observe que 1: 0sempre será a primeira linha de saída. - Isso contradiz o seu exemplo e também não faz sentido para mim.
precisa saber é o seguinte

Respostas:

6

Haskell, 184 caracteres

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

Isso faz uma travessia pela primeira vez da Árvore Calkin-Wilf , produzindo todos os números racionais positivos em forma reduzida exatamente uma vez. Em seguida, alterna entre positivo e negativo para cobrir todos os números racionais diferentes de zero e os blocos com zeros entre as entradas quadradas.

Saída (excluindo zero linhas por questões de brevidade):

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
fonte
5

Sábio, 103 113 128

Sage pode listar os racionais com facilidade! A formatação para atender aos requisitos do programa, como sempre, estraga tudo.

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 enumera de QQacordo com a sua altura : o valor absoluto máximo do numerador e denominador após a redução do MDC.

boothby
fonte
Você pode eliminar o x.next()e usar printapenas uma vez, como se segue, levando o placar até 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. Isso não é exibido corretamente em um comentário, mas acho que você pode entender o que quero dizer.
Res
BTW, notei que, após os 4 primeiros elementos positivos, a enumeração de Sage não é a mesma das outras respostas. As fórmulas de Calkin-Wilf fornecem uma sequência na qual o denominador de um racional é o numerador do próximo racional; por exemplo (..., 1/3, 3/2, 2/3, ...), em comparação com o de Sage (..., 1/3, 3/1, 2/3, ...). Não consigo localizar nenhuma documentação para a enumeração de Sage, para ver como é computada.
res
@res, obrigado! Eu queria mesclar as instruções de impressão, mas esqueci de usar a notação [x..y]. Ótimo ver outro usuário do Sage aqui!
Boothby
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

Isso usa a recursão dada em Recounting the Rationals por Calkin & Wilf.

res
fonte
2

Haskell, 55 bytes

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

saídas

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

1% 1 é a raiz da árvore Calkin-Wilf; o iterado adiciona os dois filhos de cada nó; a junção recolhe os níveis em uma única lista.

120 caracteres se você adicionar importações, 0 e negativos adequados:

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

saídas

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
...

saída de slots vazios? isso é de mau gosto :( você me colocou na "lista de todos os argumentos positivos"

John Tromp
fonte
mapM_ print$fix((1%1:).(>>= \x->[x+1,1/(x+1)]))é de 47 caracteres. de haskellwiki . funciona como é, sem qualquer importação, pelo haskell.org 's 'experimentá-lo' REPL (bem, sem a mapM_ printparte ...)
Will Ness
1

PHP 105 bytes

Nota: Este código deve ser salvo como iso-8859-1 (ansi) para funcionar corretamente. Os interpretadores online que codificam toda a entrada para utf8 por padrão (como ideone) geram a saída errada.

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

Usando a enumeração de Georg Cantor (ligeiramente modificada para +/- valores).

Se estiver com problemas para executar o código acima (provavelmente devido a uma quantidade excessiva de mensagens AVISO), use-o (107 bytes):

<?for($f=µ;$i++<$j*$j||++$j%2||(--$$f?$$f--:$f^=C);)echo"$i: ",$i==$j*$j?$j%2?$x=++$ö.'/'.++$µ:"-$x":0,'
';
primo
fonte
1
Eu recebo erros em tempo de execução com esse código (que parece conter alguns caracteres estranhos; por exemplo, "$ ö. ~ Ð.").
res
Você pode demonstrar que essa solução funciona, digamos, em ideone? Também recebo erros: ideone.com/ru1fo
mellamokb
A idéia parece errar quando são geradas muitas mensagens AVISO: ~ Ð (igual a '/') e ~ õ (igual a "\ n") geram um aviso a cada iteração. Obviamente, se você tiver os AVISOS desativados, isso não será um problema. Uma pasta com os dois substituídos (107 bytes): ideone.com/lFUbl
primo
Acabei de notar que o interpretador PHP da Ideone gera a saída errada. Se você executar o código localmente, verá que está correto. Ou, você pode testá-lo com um interpretter PHP válido, como verificador de desempenho do Anarchy Golf: golf.shinh.org/checker.html (salve-o em um arquivo e upload)
primo
Quando eu salvo o seu código revisado em um arquivo com codificação ANSI, ele é executado no intérprete do Anarchy Golf. No entanto, agora existe um problema diferente: viola o requisito de que "nenhum número racional diferente de zero deve aparecer duas vezes" na lista. De fato, o código parece listar todos os racionais infinitamente muitas vezes; por exemplo, 1/1, 2/2, 3/3, ... são todos iguais, e da mesma forma para 1/2, 2/4, 3/6, ..., etc.
res
0

Oitava, 168 bytes

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

A solução não é muito sofisticada, é apenas uma simples passagem diagonal do "tapete" de números racionais, descartando todas as frações que podem ser simplificadas. Depois de um número positivo a/b, seu oposto -a/bé sempre impresso antes do próximo da sequência.

Passagem diagonal de todos os racionais positivos

Como todas as frações simples positivas serão impressas e as frações assinadas opostas àquelas serão impressas, e nunca é possível que duas frações simples diferentes tenham o mesmo valor, cada número racional diferente de zero será impresso exatamente uma vez.

Degolfado:

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
fonte