さて、無駄話で発展してきたBasicで書かれた素因数分解プログラムのC#への移植について進めてゆきましょう。
まずはオリジナルのプログラム(注)をストレートにC#に変えてゆきます。
注:前回からの抜粋「このプログラムは、100~330行迄、数値をコンマ付き書式(”#,0”)で文字列として入力させるように指示し、前後の空白をトリムし、今度は折角入力させた','を削除して数値化するという前処理をして、340~430行までで素因数分解式の表示を行っています。」
/////////////
//素因数分解
/////////////
using System;
using System.Diagnostics; //Stopwatch使用の為
class PrimeFactor
{
static void Main()
{
//コンソールへの入力指示
Console.WriteLine("素因数分解します。");
Console.WriteLine("任意の整数を入力して下さい。");
Console.WriteLine("例えば、12,319と入力してEnterします。");
Console.WriteLine("あるいは、99,400,891とか6,041,375,340とか、");
Console.WriteLine("123,456,789,013、123,456,789,011(素数)、761,838,257,287(素数)など入力してEnterします。");
//変数nyulの入力
string nyul = Console.ReadLine();
//入力データnyulの両端の空白を除去
nyul = nyul.Trim();
//入力データnyulから、カンマを削除した数値に変換
nyul = nyul.Replace(",", "");
int n = 0;
Int32.TryParse(nyul, out n);
//Stopwatchクラス生成
Stopwatch sw = new Stopwatch();
//開始
sw.Start();
Console.Write("{0} = ", String.Format("{0:#,0}", n)); //String.Format()による3桁毎のカンマ書式
//素因数分解用変数
int a;
do
{
a = 2;
while(a <= Math.Sqrt(n))
{
if(n % a == 0)
{
Console.Write(" {0} * ", String.Format("{0:#,0}", a));
n /= a;
break;
}
a += (a > 2) ? 2 : 1; //aが2なら+1、3以上なら+2する
}
} while(a <= Math.Sqrt(n)); //IF A > SQR(N) THEN *EXITの代替
Console.WriteLine("{0}", String.Format("{0:#,0}", n));
//終了
sw.Stop();
//所要時間表示
TimeSpan ts = sw.Elapsed;
Console.WriteLine("処理時間は、{0}分{1}秒{2}ミリ秒でした", ts.Minutes, ts.Seconds, ts.Milliseconds);
//DOS窓を維持
Console.ReadKey();
}
Basic プログラムの処理内容毎に色分けし、それに対応するC#プログラムも色分けしていますので何をしているのかが分かると思います。
先ず、改行付き文字列出力はConsole.WriteLine、入力はConsole.ReadLine、文字列の前後窮迫文字除去はTrimメソッド、',' 文字の除去はReplaceメソッド、最後に文字列の整数変換はInt32.TryParse(手抜きでエラー処理省略)を使っています。
次に肝心の素因数分解の部分についてですが、
(1)最初に除数として使用する変数aを宣言します。
(2)一番外側のループとして、除数aを2に初期化する「対象値nの平方根(Math.Sqrt(n))迄」を条件とするdo~whileループ「必ず1回は実行される」を作ります。
(3)次にこのループの中に同じ条件の除数aを2に初期化しないループを作り、
(4-1)対象値nをaで除した剰余が0(C++だと"!(n % a)"と書きたくなりますね)、即ち 除算が出来れば "(除数) *" と出力し、対象値nをaで除してbreakで内側ループを抜け、新しい対象値nで同じ処理を続けます。
(4-2)対象値nをaで除算が出来なければ除数aを1または2増やします。(if文を使っても書けますが、ここでは「(条件式)? (trueの場合の値):(falseの場合の値)」を使っています。)
(5)aが増えてゆき、外側のループから抜け出した際にn(素数となる)を表示します。
という処理を行っています。一応パフォーマンスを調べるためにタイムを計っています。
実は、この処理を見ていて「再帰」でも処理できるよね、ということで、↑の赤字部分を外部関数PrintFactor(n);に置き換えてみました。PrintFactor関数の処理は同じコードとforループも試しましたが、サンプルの"987,654,321,987"で2マイクロ秒、↑のプログラムより1マイクロ秒遅かったです。再帰呼び出しによるスタック処理が多くなるためでしょうか?
//xがiで割り切れるれるか否かを調べ、割り切れる限り因数を出力し、最後にiで除されたxを出力
//なお、素数はiで除算されないのでそのままとなる
static void PrintFactor(double x)
{
double i = 2;
do
{
//因数iで割り切れれば、除算後のxで同様の処理を行う
if(x % i == 0)
{
Console.Write("{0:#,0} * ", i);
//Console.Write("{0} * ", String.Format("{0:#,0}", i)); //これと同じ
x /= i;
PrintFactor(x);
return;
}
i += (i > 2) ? 2 : 1; //aが2なら+1、3以上なら+2する
} while(i <= Math.Sqrt(x)); //xの因数の最大値まで調べる
//(iで除算可能であれば除算後の)素数xを表示
Console.WriteLine("{0:#,0}", x);
}
}
お口汚しでした。





