0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事は ひとりアドベントカレンダーRosettaCodeで楽しむプログラミング Advent Calendar 2025の5日めの記事です。

英文タイトル長いのよ。

双子素数とは、$p$ と $p + 2$ がどちらも素数であるような $p$ と $p+2$ 両方のこと。
この問題では、双子を揃えて使う必要はないので、3,5 ゆえに双子素数である 3 と、5,7 ゆえに双子素数である 7 を使って 3 + 7 = 10 とするのもよし、双子素数である 5 を使って 5 + 5 = 10 とするのもよし、という条件。
この条件で、どうやっても作れない偶数を見つけよ、というのがお題。

タスク

  1. 5,000以下の 双子素数ふたつの和で表せない偶数 を全て挙げよ。
  2. 1を素数に加える(そして3は素数なので1は双子素数にも追加される)としたらどうなるか。
  3. (ボーナス)5,000超にはそのような数はないとされていることを、10,000まで、あるいはそれ以上好きなところまでの範囲で確認せよ。(少なくとも $10^9$ までは確認されている。)

考える

とりあえず双子素数の無限リストを作ろう。

import Data.Numbers.Primes

twinPrimes :: [Int]
twinPrimes =
  [ q
  | (p,q,r) <- zip3 (1:primes) primes (tail primes)
  , p + 2 == q || q + 2 == r]

2列でやろうとするとプログラムがバタバタするので3列でやるのが正解。

ghci> take 20 twinPrimes
[3,5,7,11,13,17,19,29,31,41,43,59,61,71,73,101,103,107,109,137]

指定の上限までについて、双子素数2つの全ての組み合わせで足してみて、チェックの付かなかった偶数を取り出すのがタスク共通の仕事。
表には配列を用いるが、奇数の添え字の空間が無駄なので半分にして節約しよう。

import Data.Array.Unboxed
import Data.List

-- 上限ubまでの偶数で、双子素数リストpsを使って作れないものを見つける
engine :: Int -> [Int] -> [Int]
engine ub ps = map ((2 *) . fst) $ filter snd $ assocs checks
  where
-- tailsを使い、足して ub を超えない組み合わせでの和を全て作る
    pqss = takeWhile (not . null)
           [ takeWhile (ub >=) $ map (p +) pqs
           | pqs@(p:_) <- tails ps ]
-- pqssに出現しなかった偶数を見つける
    checks :: UArray Int Bool
    checks = accumArray (flip const) True (1, div ub 2)
             [(div pq 2, False) | pqs <- pqss, pq <- pqs]

task1 = engine 5000 twinPrimes

task2 = engine 5000 (1 : twinPrimes)

task3 = dropWhile (5000 >) $ engine 10_000 twinPrimes
ghci> task1
[2,4,94,96,98,400,402,404,514,516,518,784,786,788,904,906,908,1114,1116,1118
,1144,1146,1148,1264,1266,1268,1354,1356,1358,3244,3246,3248,4204,4206,4208]
(0.04 secs, 15,478,656 bytes)
ghci> task2
[94,96,98,400,402,404,514,516,518,784,786,788,904,906,908,1114,1116,1118,1144
,1146,1148,1264,1266,1268,1354,1356,1358,3244,3246,3248,4204,4206,4208]
(0.03 secs, 10,730,200 bytes)
ghci> task3
[]
(0.05 secs, 32,684,624 bytes)
ghci> dropWhile (5000 >) $ engine 100_000 twinPrimes
[]
(2.07 secs, 895,809,368 bytes)

タスク3は上限を10倍にするたびにかかる時間は100倍になる。

OEIS

Rosetta CodeにはHaskell版はないが、OEISにはある。

--  A007534 Positive even numbers that are not the sum of a pair of twin primes.
import qualified Data.Set as Set (map, null)
import Data.Set (empty, insert, intersection)
a007534 n = a007534_list !! (n-1)
a007534_list = f [2, 4..] empty 1 a001097_list where
   f xs'@(x:xs) s m ps'@(p:ps)
     | x > m = f xs' (insert p s) p ps
     | Set.null (s `intersection` Set.map (x -) s) = x : f xs s m ps'
     | otherwise = f xs s m ps'

-- A001097 Twin primes.
a001097 n = a001097_list !! (n-1)
a001097_list = filter ((== 1) . a164292) [1..]

-- A164292 Binary sequence identifying the twin primes (characteristic function of twin primes: 1 if n is a twin prime else 0).
a164292 1 = 0
a164292 2 = 0
a164292 n = signum (a010051' n * (a010051' (n - 2) + a010051' (n + 2))) -- ダッシュ付きは意味不明

-- A010051 Characteristic function of primes: 1 if n is prime, else 0.
import Data.List (unfoldr)
a010051 :: Integer -> Int
a010051 n = a010051_list !! (fromInteger n-1)
a010051_list = unfoldr ch (1, a000040_list) where
   ch (i, ps'@(p:ps)) = Just (fromEnum (i == p),
                              (i + 1, if i == p then ps else ps'))

--  A000040 The prime numbers.
import Data.List (genericIndex)
a000040 n = genericIndex a000040_list (n - 1)
a000040_list = base ++ larger where
    base = [2, 3, 5, 7, 11, 13, 17]
    larger = p : filter prime more
    prime n = all ((> 0) . mod n) $ takeWhile (\x -> x*x <= n) larger
    _ : p : more = roll $ makeWheels base
    roll (Wheel n rs) = [n * k + r | k <- [0..], r <- rs]
    makeWheels = foldl nextSize (Wheel 1 [1])
    nextSize (Wheel size bs) p = Wheel (size * p)
        [r | k <- [0..p-1], b <- bs, let r = size*k+b, mod r p > 0]
data Wheel = Wheel Integer [Integer]

書いたのは、まぎれもないヤツさ。

ghci> take 35 a007534_list
[2,4,94,96,98,400,402,404,514,516,518,784,786,788,904,906,908,1114,1116,1118
,1144,1146,1148,1264,1266,1268,1354,1356,1358,3244,3246,3248,4204,4206,4208]
(0.38 secs, 163,941,504 bytes)

何をしているのだろう?コードを整理して読み解く。

a007534_list = f [2, 4..] empty 1 twinPrimes
  where
    f xxs@(x:xs) s m pps@(p:ps)
      | x > m     = f xxs (insert p s) p ps
      | cond      = x : f xs s m pps
      | otherwise = f xs s m pps
      where
        cond = Set.null (s `intersection` Set.map (x -) s)

xxs 偶数リスト 双子素数の足し合わせで作れるか判定されてリストに送出される情報源

s $S$ これから検討する $x$ 以下の双子素数全ての集合(Set Integer)。そうなるように最初の等式で維持する。
m s を管理するための補助変数、s の最大値(厳密には、x以上の最小の素数となる)
pps 双子素数リスト 使うタイミングになったら s に順に移される

cond 今回の $x$ に関して、$p \in S$ で $x - p \in S$ を満たすものがない、つまり、($x$ 以下の)双子素数の足し合わせで表現できないとき真
真なら x は2つめの等式で出力され、さもなくば3つめの等式でスルーされる

個人的には xxs は最後の引数にして欲しいな、とかあるけど、まぁこんなものか。

cond も「共通部分が空集合」より「条件を満たす要素が存在しない」 $\forall p \in S; x - p \not\in S$

cond = all (\p -> notMember (x - p) s) (Set.elems s)

の方が判りやすくないかしら。

今日はここまで。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?