Вывести список всех рациональных чисел

13

Из всей математики всегда будет несколько теорем, которые выходят за рамки всего здравого смысла. Одним из них является тот факт, что существуют разные размеры бесконечности. Другим интересным фактом является идея о том, что многие бесконечности, которые кажутся разными, на самом деле имеют одинаковый размер. Четных чисел столько же, сколько и целых, и рациональных чисел.

Общая концепция этого вопроса - противостоять странной реальности бесконечности. В этой задаче ваша программа выведет список, который будет:

  • В любой конкретный момент времени всегда есть целое количество записей
  • В конце концов, содержать (если оставить достаточно долго) любое конкретное (ненулевое) рациональное число точно один раз во всем списке
  • Содержат неограниченное количество пустых слотов (записи в списке, которые излишне установлены в 0)
  • Есть доля пустых слотов, которая приближается к пределу 100%
  • Для каждого положительного целого числа N, иметь бесконечное количество мест с N последовательных пустых слотов

Соревнование

Ваша задача - написать ту самую короткую из возможных программ, которая выведет специальный список со следующими правилами:

  1. Все записи с индексом, который не является квадратным числом, должны быть установлены в ноль. Итак, первая запись будет отлична от нуля, вторая и третья будут равны нулю, четвертая будет отлична от нуля и т. Д.
  2. Все рациональные числа будут в форме неправильной дроби (такой как 4/5 или 144/13), которая была упрощена. Исключение составляют нули, которые будут просто 0.
  3. Все (положительные и отрицательные) рациональные числа должны в конечном итоге появиться в списке, если ваша программа работает достаточно долго и с достаточным объемом памяти. Для любого конкретного рационального числа требуемое время может быть произвольно большим, но всегда конечным количеством времени.
  4. Если запустить в течение бесконечного количества времени, никакое ненулевое рациональное число никогда не должно появляться дважды.

Правило 3 допускает некоторые вариации, поскольку существует бесконечное количество различных возможных юридических результатов.

На выходе будет поток строк. Каждая строка будет иметь общий вид, 5: 2/3где первое число является номером записи, затем следует рациональное число. Обратите внимание, что 1: 0всегда будет первая строка вывода.

Пример фрагмента вывода:

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

Правила, положения и примечания

Это код гольф. Применяются стандартные правила игры в гольф. Кроме того, из-за различий, разрешенных в выходных данных, вам необходимо хотя бы показать, почему вы считаете, что ваш список будет содержать все возможные рациональные числа ровно один раз и что ваше решение является правильным.

РЕДАКТИРОВАТЬ: Поскольку простые числа отвлекают от задачи, я изменяю его на квадратные числа. Это выполняет ту же цель, а также сокращает решения.

PhiNotPi
источник
1
Какой смысл правила 1? Вы просто хотите, чтобы люди играли в две разные программы, чтобы проверить первичность и перечислить рациональные значения?
Питер Тейлор
Он показывает, как очень небольшая часть целых чисел по-прежнему имеет ту же мощность, что и полный набор рациональных чисел, а также позволяет проценту пустых слотов приближаться (но никогда не достигать) к 100%.
PhiNotPi
Я предполагаю, что программа также должна работать в фиксированном объеме памяти, то есть она не может предположить, что машина всегда может выделить больше? Кроме того, противоречит ли правила использованию (скажем) C int для индекса списка, когда вы знаете, что он имеет конечный диапазон? (Даже если точный предел может варьироваться в зависимости от реализации.) Требуется ли какая-либо форма bignum?
хлебница
1
@PhiNotPi, есть гораздо более простые способы сделать это, и это отвлекает от более интересной части вопроса.
Питер Тейлор
1
Обратите внимание, что 1: 0всегда будет первая строка вывода. - Это противоречит вашему примеру и также не имеет смысла для меня.
Wrzlprmft

Ответы:

6

Хаскель, 184 персонажа

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

Это делает обход дерева Калкин-Уилф в ширину , приводя все положительные рациональные числа в приведенной форме ровно один раз. Затем он чередуется между положительным и отрицательным, чтобы покрыть все ненулевые рациональные числа и площадки нулями между квадратными записями.

Вывод (исключая ноль строк для краткости):

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
...
Хаммар
источник
5

Шалфей, 103 113 128

Мудрец может с лёгкостью перечислить рациональное! Форматирование в соответствии с требованиями программы, как всегда, все разрушает.

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]

Мудрец перечисляет в QQсоответствии с их высотой : максимальное абсолютное значение числителя и знаменателя после сокращения GCD.

Бутби
источник
Вы можете устранить x.next()и использовать printтолько один раз, а именно, доведя счет до 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. Это не отображается должным образом в комментарии, но я думаю, вы можете понять, что я имею в виду.
Res
Кстати, я заметил, что после первых 4 положительных элементов, перечисление Мудреца не совпадает с другими ответами. Формулы Калкина-Уилфа дают последовательность, в которой знаменатель рационального является числителем следующего рационального; например (..., 1/3, 3/2, 2/3, ...), по сравнению с Sage (..., 1/3, 3/1, 2/3, ...). Я не могу найти какую-либо документацию для перечисления Sage, чтобы увидеть, как она вычисляется.
Res
@res, спасибо! Я хотел объединить операторы печати, но забыл использовать нотацию [x..y]. Рад видеть другого пользователя Sage здесь!
Boothby
4

Питон, 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

Для этого используется рекурсия, приведенная в « Пересчете рационалов » Calkin & Wilf.

Рез
источник
2

Haskell, 55 байт

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

выходы

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

1% 1 является корнем дерева Калкина-Уилфа; итерация добавляет обоих потомков каждого узла; объединение сворачивает уровни в один список.

120 символов, если вы добавите правильный импорт, 0 и негативы:

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

выходы

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

выводить пустые слоты? это дурной вкус :( Вы были со мной в "списке всех положительных доводов"

Джон Тромп
источник
mapM_ print$fix((1%1:).(>>= \x->[x+1,1/(x+1)]))47 символов. из haskellwiki . работает как есть, без какого-либо импорта, на REPL "попробуй" на haskell.org (ну, без mapM_ printчасти ...)
Will Ness
1

PHP 105 байт

Примечание: этот код должен быть сохранен как iso-8859-1 (ansi) для правильной работы. Онлайн-интерпретаторы, которые по умолчанию кодируют весь ввод в utf8 (например, ideone), будут генерировать неправильный вывод.

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

Использование перечисления Георга Кантора (немного изменено для значений +/-).

Если у вас возникли проблемы с запуском приведенного выше кода (вероятно, из-за чрезмерного количества сообщений NOTICE), используйте вместо этого (107 байт):

<?for($f=µ;$i++<$j*$j||++$j%2||(--$$f?$$f--:$f^=C);)echo"$i: ",$i==$j*$j?$j%2?$x=++$ö.'/'.++$µ:"-$x":0,'
';
Примо
источник
1
Я получаю ошибки во время выполнения с этим кодом (который, кажется, содержит некоторые странные символы; например, "$ ö. ~ Ð.").
Res
Можете ли вы продемонстрировать, что это решение работает, скажем, на ideone? Я также получаю ошибки: ideone.com/ru1fo
mellamokb
Кажется, что Ideone выдает ошибку, когда генерируется слишком много сообщений NOTICE: и ~ Ð (равно '/'), и ~ õ (равно "\ n") будут генерировать NOTICE при каждой итерации. Конечно, если у вас отключены уведомления, это не проблема. Паста с заменой обоих (107 байт): ideone.com/lFUbl
primo
Я только что заметил, что PHP интерпретатор Ideone генерирует неправильный вывод. Если вы запустите код локально, вы увидите, что он правильный. Или вы можете протестировать его с помощью действующего интерпретатора PHP, такого как средство проверки производительности Anarchy Golf: golf.shinh.org/checker.html (сохранить его в файл и загрузить)
primo
Когда я сохраняю ваш исправленный код в файл с кодировкой ANSI, он запускается на интерпретаторе Anarchy Golf. Однако теперь есть другая проблема: это нарушает требование, что «не ненулевое рациональное число никогда не должно появляться дважды» в списке. Фактически, код, кажется, перечисляет каждое рациональное бесконечно много раз; например , 1/1, 2/2, 3/3, ... все же рационально, а также для 1/2, 2/4, 3/6, ... и т.д.
рес
0

Октава, 168 байт

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/bпротивоположное -a/b, прежде чем идет следующий из последовательности.

Диагональный обход всех положительных рациональных чисел

Поскольку все положительные простые дроби будут напечатаны, а противоположные подписанные дроби будут напечатаны, и две разные простые дроби никогда не смогут иметь одинаковое значение, каждое ненулевое рациональное число будет напечатано ровно один раз.

Degolfed:

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
источник