Сито сундарама-список понимания

Я пытаюсь написать функцию, которая вычисляет всех нечетных чисел от 1..n используя "решето Сундарама".

вот моя попытка:

sSund :: Integer -> [Integer]
sSund n = [ i * 2 + 1 | i <- [1..n], j <- [f i], (i + j + 2 * i * j) > n ] 
  where f 1 = 1 
        f y = y + 1 --use function f because i don't know how insert 1 into j's list

но он дает некоторые неправильные числа, такие как 9,15,21,25 и т. д.

*Main> sSund 30
[7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61]

что я делаю не так?

1 ответов


как это работает

seive Сундарама работает, фокусируясь на нечетных числах 2n+1 и исключая те, которые являются произведением чисел.

если два числа умножаются, чтобы сделать нечетное число, они должны быть нечетными, поэтому наше число 2n+1 = (2i+1) (2j+1). Если мы умножим, что мы выходим из 2n+1 = 4ij + 2И +2Д + 1, который можно упростить до 2n=4ij+2И+2дж, что опять же упрощает до n=2ij+я+Дж. Поэтому мы не хотим, если мы можем написать его как 2ij+я+Дж. Это верно для любых чисел I и j, но это нормально, просто избавиться от тех, где i

исправление кода

в вашем коде, вы создаете некоторые цифры i + j + 2 * i * j быть исключенным, но вы на самом деле просто исключаете i вместо i + j + 2 * i * j. The j<-[f i] просто дает вам один j значение в списке вместо всех чисел из i до n, который вы должны написать как [i..n].

это много проще всего сначала создать список исключений:

sSundDelete :: Integer -> [Integer]            
sSundDelete n = [i+j+2*i*j|i<-[1..n], j<-[i..n]]

здесь я решил просто разрешить i и j между 1 и n, потому что в противном случае 2ij+i+j определенно больше, чем n.

теперь мы можем составить список чисел x которые не включают эти числа, а затем делают их нечетными с помощью формулы 2*n+1:

sSund :: Integer -> [Integer]
sSund n = let del = sSundDelete n in
     2:[2*x+1 | x <- [1..n], not (x `elem` del)]

, который правильно дает вам

> sSund 30
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61]

ускорение вещи вверх!--37-->

это не так быстро, как могло бы быть, потому что, если вы посмотрите на

> sSundDelete 10
[4,7,10,13,16,19,22,25,28,31,12,17,22,27,32,37,42,47,52,24,31,38,45,52,59,66,73,40,49,58,67,76,85,94,60,71,82,93,104,115,84,97,110,123,136,112,127,142,157,144,161,178,180,199,220]

он имеет номера гораздо больше, чем нам нужно - sSund 10 доходит только до 2*10+1=21. Это означает, что мы проверяем наши номера снова и снова против номеров, которые мы не рассматривали в любом случае!

самое простое, что нужно сделать, это переписать sSundDelete сказать

sSundDelete n = [i+j+2*i*j|i<-[1..n], j<-[i..n],i+j+2*i*j<=n]

так же, как и ты, или

sSundDelete n = filter (<= n) [i+j+2*i*j|i<-[1..n], j<-[i..n]]

используя немного математики, чтобы ускорить

проблема в том, что они генерируют слишком много чисел, а затем выбрасывают их. Было бы быстрее генерировать только те числа, которые нам нужны.

на самом деле, я думаю, что лучше рассчитать, как далеко идти. самый маленький j мы всегда будем использовать is i, так что наименьшее, что 2ij+i+j может быть 2i2+2i. Если мы не хотим, чтобы это было над n, мы хотим 2i2+2i2i <= floor (sqrt (fromIntegral n / 2)) (floor усекает десятичные дроби, так что floor 35.7 и в 35, и fromIntegral используется здесь для преобразования n к числу с плавающей запятой (разрешая нецелые числа), поэтому мы можем делать деление и квадратные корни.

это было много работы, но теперь мы можем просто вычислить один раз, насколько большой i должны go:

sSundDelete n = filter (<= n) [i+j+2*i*j|i<-[1..floor (sqrt (fromIntegral n / 2))], j<-[i..n]]

мы можем сделать аналогичную работу на j. Мы хотим 2ij+i+jj<=floor( (n'-i')/(2*i'+1)) здесь i'=fromIntegral i и n'=fromIntegral n. Это дает нам

sSundDelete n = [i+j+2*i*j|let n'=fromIntegral n,
                           i<-[1..floor (sqrt (n' / 2))],
                           let i' = fromIntegral i,
                           j<-[i..floor( (n'-i')/(2*i'+1))]]

это делает его достаточно быстрым для меня, чтобы не отказаться от ожидания sSund 5000 для вычисления второго простого числа!