R.Tanaka.Ichiro's Blog

主にC# な話題です

目次

Blog 利用状況

ニュース

Rさんのふるい

以前社内での会話の流れで「素数を列挙するプログラミングってどう書くの?」と言われた。
そこで、その場で書いて見せた。


static void Main(string[] args) {
  for(var i = 0; i < 20; ++i) {
    if (IsPrimeNumber(i)) Console.WriteLine(i);
  }
  Console.ReadKey();
}
static bool IsPrimeNumber(int value) {
  if (value < 2) return false;
  for(var i = 2; i <= value / 2; ++ i) {
    if (value % i == 0) return false;
  }
  return true;
}


上記のコードを見てもらえば一目瞭然だとは思うけれど、僕は数学が苦手だ。

「これって、どういう計算しているの?」と聞かれたので、
「単純に総当たりで剰余が出たら素数じゃないと判定している」と言うと、
「エラトステネスのふるい」という方法を教わった。

1.ある数字までの表を作る
2.そこから偶数を除外する
3.残った数値を昇順に順次取り出して、その数値の倍数を除外する
4.ただし、3を実行するのは、取り出した数が調べたい数の平方根になるまでで良い

確か「偶数は素数ではないので奇数のみが判定の対象」で「素数の倍数は素数ではない」という点から、この条件に一致するものを除外するということだったかと。

で、先日ちょっと暇があったり、出水氏の以下のエントリで思い出したりしたので、自分で書いてみた。
http://blogs.wankuma.com/izmktr/archive/2008/10/01/157895.aspx


static void Main(string[] args)
{
  var t = エラトステネスのふるい(20);
  Console.WriteLine("おっす、オラ素数!"); // 本当は、これが言いたかっただけ
  for(var i = 0; i < t.Length; ++i) {
    if (!t[i]) Console.WriteLine(i);
  }
  Console.ReadKey();
}
static bool[] エラトステネスのふるい(int max) {
  var n = new bool[max];
  n[0] = n[1] = true;
  for(var i = 4; i < n.Length; i += 2) n[i] = true;
  for(var i = 3; i * i < max; ++i) {
    if (!n[i]) for(var j = i; j < max / i; j += 2) n[i * j] = true;
  }
  return n;
}


実行結果は、以下の通り・・・


おっす、オラ素数!
2
3
5
7
11
13
17
19


何も考えずに書いてみたので、メモリの大きさにかなり依存する。
もう少し改良したいけど・・・

投稿日時 : 2008年10月14日 11:30

Feedback

# re: Rさんのふるい 2008/10/14 12:06 じゃんぬねっと

> おっす、オラ素数!

噴かせるなww

# re: Rさんのふるい 2008/10/14 12:47 HiJun

せんせいー!!!質問です。

1って素数の分類に含まれますか?

# re: Rさんのふるい 2008/10/14 15:18 ぽぴ王子

> 噴かせるなww
このコメントの方で噴いちゃったジャマイカwww

> Rさんのふるい
これって「おっす、オラ素数!」というネタが(ドラゴン
ボールが連載終了して久しいこともあって)Rさんの(ネタ
は)古いと言いたいんですよね。
わかってます。大丈夫です。

# re: Rさんのふるい 2008/10/14 16:00 επιστημη

まぢれす。

>1って素数の分類に含まれますか?
1を素数にすると素因数分解の結果が一意じゃなくなるのでペケです。

# re: Rさんのふるい 2008/10/14 17:13 みきぬ

> おっす、オラ素数!

発音してみると、違和感なさすぎて噴いたw

# re: Rさんのふるい 2008/10/14 17:41 επιστημη

> メモリの大きさにかなり依存する。

BitArray使えばかなりケチれそーですねぃ。

# re: Rさんのふるい 2008/10/15 13:58 R・田中一郎

じゃんぬねっと さん

>噴かせるなww

オヤジギャグな気もしたのですが、言わずにはいられないのです。

------------------------------
HiJun さん

>1って素数の分類に含まれますか?

素数ではないので、わざわざ 1 以下だけ特別な処理をしているのです。

------------------------------
ぽぴ王子 さん

>ボールが連載終了して久しいこともあって)Rさんの(ネタ
>は)古いと言いたいんですよね。

では、今日は新しいアニメネタのエントリにします。


------------------------------
επιστημη さん

>>1って素数の分類に含まれますか?
>1を素数にすると素因数分解の結果が一意じゃなくなるのでペケです。

なるほど、そういうことなんですか。

>BitArray使えばかなりケチれそーですねぃ。

こんなクラスがあったとはっ!

------------------------------
みきぬ さん

>発音してみると、違和感なさすぎて噴いたw

「ラムダっちゃ」の時は、皆さん冷たかったので、今回もドキドキだったのですが、噴いてもらえて良かったです。

# re: Rさんのふるい 2008/10/15 14:04 渋木宏明(ひどり)

LINQ で書かないの?

# re: Rさんのふるい 2008/10/16 13:52 R・田中一郎

LINQ だと、処理コストを考慮しながら書かないといけないかなーと思ったのです。
LINQ だとふるいにかけないで、素数ではない数値をコレクションに格納していく方法のほうが向いているかな?

# re: Rさんのふるい 2008/10/17 0:20 通りがかりの数学好きなおっさん・ふにふに

素数の一意性について。素数じゃない数字を合成数という。
あらゆる自然数は、この合成数か素数のいづれか。

素数の定義として、1とその数自身でしか割れないもの。
つまりどんなに頑張っても(1×なんたら)でしか表現
できない。2=1×2,3=1×3,7=1×7,31=1×31,97=1×97

ところが30=3×10とも言えるし、30=6×5とも表現可。
もっと細分化すると30=2×3×5。素数の積で合成(構成)
この、これ以上細かくできない表し方を素因数分解。
いわば、素(もと)となる因子のみの積で表された状態。
30という数字は、2と3と5を最も小さな素材とする合成数。

30=(1×2)×(1×3)×(1×5)って書いても間違いでは
ないが、くどいだけ。なので1は特別な存在として認めて
あげなくてはならない。
これが素数の一意性保持のための1排除の理由。

30とは、1と2と3と5を素材とする合成数とゆってしまうと
2と3と5のキャラが薄くなってしまう。つまり
2と3と5の一意性=最小素材っぽさ、が失われる。

そもそも素数の仲間として1を迎え入れた時点で、
素数とは1以外にはない。という、変てこな事態になる。

何にでも醤油をかければいいという人がいるが、数学の
世界も然り。何にでも1を入れとけばいいってのは、
ずるいし、素敵でもないし、さわやかでもない。


# re: Rさんのふるい 2008/10/17 3:01 ふにふに

素因数分解の一意性の間違いですたm(_ _)m
素数なら、素数の既約性というべきか。

プログラムのことはよく分かりません。間違ってたらごめんなさい。

手順3でいうところの、偶数排除から残った数字を昇順に
取り出して、その倍数を消して行くで思ったのですが、
このコードって、ダブりな「消し」を許してるんでしょうか
例えば9の倍数をカットするくだりとか。

仮に1~100までの表から素数のみを取り出す=
合成数を削除していって残ったものが素数

というエラトステネスの篩いをさせた場合

まず1は素数でないから削除
2は素数だから残しつつ2の倍数=偶数全消し
3は残しつつ3の倍数(3×2,3×3,3×4,3×5,3×6,3×7…)
100超えたら次の段ルール
5は残しつつ(5×2,5×3,5×4,5×5,5×6,5×7…)
7は残しつつ(7×2,7×3,7×4,7×5,7×6,7×7…)
9は消しつつ(9×2,9×3,9×4,9×5,9×6,9×7…)
11は残しつつ…(延々)

ふと7の段を見てみると実に無駄が多いように見える。
7×7以外は、その前の段階でガイシュツ。
重複消しを許さないという観点から見ると、
5の段の5×2,5×3,5×4,5×6もガイシュツ。
3の段の3×2,3×4,3×6もガイシュツ。
9の段にいたっては、全てが3の倍数カット時にガイシュツ

「重複消し、ぜってぇ許さねぇ。重複したら殺す。」
みたいなルールだと、以下のような消し方なら殺されないで
済みそうです。

まず1は素数でないから削除
2を残し、偶数全削除
3を残し、3×3,3×5,3×7,3×11,3×13,…100超えたら
5を残し、5×5,5×7,5×11,5×13,…100超えたら
7を残し、7×7,7×11,7×13,…100超えたら
11を残し…

2以降の段は、実質的な削除スタートポイントは、n×nで
よさそう。でもって「素数の素数倍」のみを消して行けば
殺されずに済みそう。
そもそも合成数は素数×素数で成り立っているし。

それなのでこの削除スタートのn×nが100を超えてしまった
ら、それ以上やる必要がなくなり、重複を許すことなく
全ての合成数を消せたということになる。

10×10=100ですが、そもそも10が合成数なので、
10の段はやらんでよく、9や8に至っても素数でない上、
すでにやれら判定。それらの素数倍もなおさら当たり判定。
よって7の素数倍の段までやればよく、
11の段のスタートは11×11=121ですでにオーバー。
7×7,7×11,7×13=91まで(ちなみに7×14=98はガイシュツ)

それなので元々の表で言うところの最大の数、100の
平方根10を超えない、最大の素数の「素数倍」の段まで
削除すれば、それ以上は削除する意味はなく、
仮に1~1000までの表だとしたら、1000のルートの31.6…
を超えない最大素数31の素数倍の段までやればおk。

長々とすみません。重複消しを許さないコードなら
ものすごく早く終わると思ったので。
(ただ一方で、少なくとも31までの素数が既知のもの
でなくてはならない気もしてますが…3の素数倍の段つまり
…3×23,3×29,3×31みたいに、右側が知られてないと、
素数倍の設定すら出来ない?とも思えたり…
表から抽出も何も1~100の中の、1~31までの素数は
判明済みな条件付データベースでないと…)


また「表から抽出」ではなく、任意の数が素数であるか
否かの判定をするのであれば、4桁程度なら電卓1個で
面倒ないです。

1967はどうなん?みたいな時、1967のルートは44.35…
を超えない最大素数は43。1967を2で3で5で(中略)43で、
割ってみて一回でも割れたら合成数。1967は7の段階で
やられる。2で割れるかは一目瞭然なので計13回のコスト
2003はどうか?2003の平方根は44.75…。
同様に43まで。13回の割り算で一回も割れない=素数

皆様もご自分の生年だったり電話番号下4桁が果たして
素数であるか否か、電卓で確認してみては?

素数だった時は、ぺヤング開けた時のソースに
たまたまギザギザがなかった時のような(ry
板汚しスマソ。





# re: Rさんのふるい 2008/10/20 15:56 R・田中一郎

えーっと・・・乙!
後日、時間のあるときに、エントリで回答します^^;

# QeyGupyxtbyfkKOV 2011/12/16 1:37 http://www.healthinter.org/health/page/flomax.php

Thanks for the news! Just was thinking about it! By the way Happy New Year to all of you:D

# RJTqaDFLmnxUEbHY 2011/12/26 23:33 http://www.discreetpharmacist.com/ger/index.asp

I do`t see a feedback or the other coordinates from the blog administration!...

# WdhbLpPsUUbH 2011/12/27 0:16 http://www.discreetpharmacist.com/ger/index.asp

Cool:) I would say say it exploded my brain..!

# VaZiHRRsZu 2011/12/27 6:21 http://www.laurenslinens.com

The material is on the five plus. But there is a minus! My internet speed 56kb/sek. The page was loading for about 40 seconds!...

# CKCvUvLqYOu 2011/12/27 6:29 http://www.67wine.com/

Yet, much is unclear. Could you describe in more details!...

タイトル
名前
Url
コメント