素数な日付とは
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 となる。
素数な日付の探し方

計算機を使えば、ある日付が素数な日付かどうかを簡単に調べることができるし、指定された範囲に含まれる素数な日付を探すこともできる。
素数な日付を調べる手順は以下のようなものになるだろう。
- 日付を整数に変換する。
- 変換した整数が素数かどうかを判定する。
そんなにややこしい話ではない。あとは、この手順をプログラミング言語の R で実装するだけだ [2] 。以下では、2017年の素数な日付をすべて探し出す事例について紹介するが、設定を適宜変更すれば、2017年以外の素数な日付を探し出すことも可能だ。
日付の整数への変換
1個の日付だけならば、人の手でこれを整数にするのは簡単だ。2017年12月19日ならば、単に 20171219 と入力するだけだ。しかし、たくさんの日付、例えば2017年のすべての日付を整数にするとなると、人の手で行うのは難しくなる。
R においては、日付を Date
型で扱うのが楽だ。Date
型を作るための函数 as.Date()
を使えば、簡単にたくさん日付を収めたベクトルを作ることができるのだ。
ただし、Date
型は、そのままでは整数にはならないので、これを整数に変換する必要がある。日付を整数に変換する手順は、以下のようなものになるだろう。
as.Date()
を使って、Date
型の日付のベクトルを作成する。format()
を使って、年月日をくっつけた文字列のベクトルに変換する。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 # (以下略)
しかし、このように TRUE
や FALSE
が並んでいるだけでは、どこが素数な日付か分かりにくい。分かりやすくするためには、以下のようにすればよい。こうすることで、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"
を指定すれば、皇紀で考えたときの素数な日付を返す。何も指定しなければ、西暦で考えて素数な日付を返す。