Rを使って素数な日付を探す

概要
「2017年12月19日」を20171219にするなど、年月日をまとめて1つの整数にしたときに、その整数が素数となるような日付を探す方法について、プログラミング言語のRを使った事例を通じて説明する。

素数な日付とは

2017年12月19日という日付がある。これは、素数な日付だ。どういうことかと言うと、2017年12月19日の数字だけをくっつけると、20171219(二千十七万千二百十九)になる。この 20171219 という数は素数だ。つまり、20171219自身と1でしか割りきれない数だ。

このように、年月日をくっつけて1つの整数と見なしたときに、その整数が素数になるような日付のことを「素数な日付」と呼ぶことにしたい。

なお、年月日をくっつけて整数を作るとき、月や日が1桁だったら、数字の0を補うものとする。例えば、2017年8月7日ならば、201787 とするのではなく、20170807 とするということだ。同様に、1996年4月11日ならば 19960411 となるし、2295年11月9日ならば 22951109 となる。

素数な日付の探し方

例えば、2017年で素数になる日付を探してみると2017年11月1日(20171101)などが素数な日付になる。
例えば、2017年で素数になる日付を探してみると2017年11月1日(20171101)などが素数な日付になる [1]

計算機を使えば、ある日付が素数な日付かどうかを簡単に調べることができるし、指定された範囲に含まれる素数な日付を探すこともできる。

素数な日付を調べる手順は以下のようなものになるだろう。

  1. 日付を整数に変換する。
  2. 変換した整数が素数かどうかを判定する。

そんなにややこしい話ではない。あとは、この手順をプログラミング言語の R で実装するだけだ [2] 。以下では、2017年の素数な日付をすべて探し出す事例について紹介するが、設定を適宜変更すれば、2017年以外の素数な日付を探し出すことも可能だ。

日付の整数への変換

1個の日付だけならば、人の手でこれを整数にするのは簡単だ。2017年12月19日ならば、単に 20171219 と入力するだけだ。しかし、たくさんの日付、例えば2017年のすべての日付を整数にするとなると、人の手で行うのは難しくなる。

R においては、日付を Date 型で扱うのが楽だ。Date 型を作るための函数 as.Date() を使えば、簡単にたくさん日付を収めたベクトルを作ることができるのだ。

ただし、Date 型は、そのままでは整数にはならないので、これを整数に変換する必要がある。日付を整数に変換する手順は、以下のようなものになるだろう。

  1. as.Date()を使って、Date 型の日付のベクトルを作成する。
  2. format()を使って、年月日をくっつけた文字列のベクトルに変換する。
  3. as.integer()を使って、整数のベクトルに変換する。

この手順を順番に説明していこう。

日付のベクトルの作成

函数 as.Date() を使えば、簡単に日付のベクトルを作ることができる。2017年の日付をすべて収めたベクトルを作るには、以下のようにすれば良い。

dates <- as.Date(0:364, origin = "2017-01-01")

こうすることで、2017年1月1日から365日分、すなわち2017年12月31日までの日付のベクトルを作ることが dates に代入される。origin(開始日)に指定されている日付を0日目と見なすため、1:365 でなく、0:364 となっていることに注意しよう。

このベクトルの中には、以下のように日付が2017年1月1日から順に入っている。

dates <- as.Date(0:364, origin = "2017-01-01")

文字列ベクトルへの変換

このままだと、Date 型のデータなので、素数かどうかを判定できない。このため、まず年月日をくっつけた文字列のベクトルに変換する必要がある。

format() という函数を使えば、日付を整形した形で、文字列として出力することができる。具体的には以下のようにする。

dates_chr <- format(dates, "%Y%m%d")

こうやってできあがった文字列ベクトルの dates_chr には、"20170101", "20170102", "20170103"…といった文字列が順に入っている。

整数ベクトルへの変換

そうしたら、文字列ベクトルを整数ベクトルに変換しよう。整数に変換するには、as.integer() 函数を使えばよい。

dates_int <- as.integer(dates_chr)

これで、2017年のすべての日付を整数にしたものが dates_int に収められた。

素数かどうかの判定

次にすべきことは、日付から変換された整数が、素数であるかを判定することだ。

R では、numbers というパッケージに入っている isPrime() 函数を使って、与えられた整数が素数かどうかを判定することができる。もし、このパッケージがインストールされていなければ、install.packages("numbers") として、CRANからインストールしておこう。

さて、isPrime()は整数(のベクトル)を引数としてとり、その整数が素数ならばTRUEを、素数でなければFALSEを返す。isPrime() を使った簡単な事例を挙げておこう。

library("numbers")
isPrime(7)
# [1] TRUE
isPrime(54)
# [1] FALSE
isPrime(101:110)
# [1]  TRUE FALSE  TRUE FALSE FALSE
# [6] FALSE  TRUE FALSE  TRUE FALSE

7は素数なので、isPrime(7)TRUE を返す。また、54は素数でないので、isPrime(54)FALSE を返す。最後の isPrime(101:110) は、101, 102, 103, …, 110 という整数のベクトルを引数としてとり、それぞれが素数かどうかを判定している。結果を見ると、1番目・3番目・7番目・9番目が TRUE なので、101, 103, 107, 109 が素数であることが分かる。

先ほど作成した dates_int についても同様に isPrime() を使って、各々の日付が素数かどうかを判定することができる。

isPrime(dates_int)
#  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [10] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [19] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE
# (以下略)

しかし、このように TRUEFALSE が並んでいるだけでは、どこが素数な日付か分かりにくい。分かりやすくするためには、以下のようにすればよい。こうすることで、2017年の素数な日付だけが得られる。

dates_int[isPrime(dates_int)]
#  [1] 20170121 20170219 20170223 20170301 20170303 20170331
#  [7] 20170421 20170511 20170519 20170607 20170627 20170807
# [13] 20170831 20170901 20170903 20171017 20171101 20171201
# [19] 20171219

しかも、このようにすれば、2017年の素数な日付が19個あるということが一目瞭然になる。

まとめ

今までのコードをまとめておこう。

library("numbers")

dates <- as.Date(0:364, origin = "2017-01-01")
dates_chr <- format(dates, "%Y%m%d")
dates_int <- as.integer(dates_chr)
dates_int[isPrime(dates_int)]

これを実行することにより、2017年1月1日から12月31日までの素数な日付の一覧を出すことができる。

平成や皇紀に拡張する

今までは、西暦で素数な日付を考えてきた。しかし、西暦にこだわる必然性はない。例えば、平成28年12月27日の年月日をくっつければ、281227 となり、これは素数になる。つまり、この日は平成で考えると素数な日付となるわけだ。

平成で素数な日付となったとしても、西暦で素数な日付となるとはかぎらない。平成28年12月27日は、西暦にすると2016年12月27日だが、20161227 は素数でない [3]

もちろん、やや珍しいものの、西暦でも平成でも素数な日付となるような日付は存在する。例えば、20160817 も 280817 も素数なので、2016年8月17日と見なしても平成28年8月17日と見なしても素数な日付になるわけである。

平成で見たときの素数な日付も、先ほど紹介したような手順で探し出すことができる。ただし、先ほど紹介した手順では、年が西暦で表されているため、これを平成での年に換算する必要がある。換算と言ってもそんなにややこしいことではない。西暦の年から1988を引けば、それで平成の年になるのだ。この引き算をうまく組み込めば、平成で見たときの素数な日付を探し出せるのだ。

また、適切な換算を行いさえすれば、平成でなく、皇紀で見たときの素数な日付を探し出すことも可能になる。西暦の年に660を足せば、皇紀の年になるので、この換算をすればよいのだ [4]

素数な日付を判定するためのコードを、平成や皇紀にも拡張したものを以下に記す。GitHub にも同じものを挙げてある。

# Find dates which are prime number in AD, Heisei, Kouki
library("numbers") # to use "isPrime" function

# Prepare dates
start_date <- "2017-01-01"
num_of_dates <- 365 # There are 365 days in common year
dates <- as.Date(0:(num_of_dates-1), origin = start_date)


disp_prime_date <- function(x, cal="AD"){
  if(!inherits(x, "Date")){ # to avoid non-Date input
    stop("Invalid input")
  }
  y <- as.integer(format(x, "%Y")) # Year in AD
  if(cal == "Heisei"){
    y <- y - 1988
  } else if(cal == "Kouki"){
    y <- y + 660
  } else if(cal != "AD"){
    stop("Invalid cal")
  }
  int <- as.integer(paste0(y, (format(x, "%m%d"))))
  int[isPrime(int)]
}

# AD
disp_prime_date(dates)

# Heisei
disp_prime_date(dates, cal="Heisei")

# Kouki
disp_prime_date(dates, cal="Kouki")

このコードでは、函数 disp_prime_date()Date 型のデータを与えると、その中で素数な日付となっているものを返す。また、オプションとして、cal="Heisei" を指定すれば、平成で考えたときに素数な日付となるものを返すようになる。また、cal="Kouki" を指定すれば、皇紀で考えたときの素数な日付を返す。何も指定しなければ、西暦で考えて素数な日付を返す。

脚注
  1. Pixabayより JuralMin 氏のパブリックドメイン画像を使用。 []
  2. R 以外のプログラミング言語で Ruby を使って素数な日付を探した例として、tkhs Yuta氏の「8桁表示で素数日や連続素数日を探す」という報告がある。 []
  3. 20161227 = 3 × 6720409 と素因数分解できる。 []
  4. 日本が太陽暦を採用する以前のことまで考えると、この換算ではうまくいかなくなるところがあるが、そういう場合は無視するものとする。 []