第19講 魔方陣汎用的生成プログラムVer.2
第6話 仮完成に加えるちょっとした工夫とは?

仮完成では、6次魔方陣には歯が立ちません。
途中でいつも実験を打ち切ってしまうので、
推測でしかありませんが、おそらくコンピュータを1日走らせたとしても1個もできないでしょう。
ですが、ちょっとした工夫をするだけで、6次魔方陣が数秒に1個の割合で生成できるようになります。
第5話のコードを仮完成と名付けましたが、
コードの骨格部分は、第6話のものも変わりませんから、仮をとってもよいぐらいです。
ですから、第6話コードはVer.2の中のわずかなバージョンアップです。
でもわずかなバージョンアップで最初の1個の生成に関しては、
生成時間は、何十万倍分の一に圧縮されます。
ですが、Ver.2の中ではどのようにコードをいじっても8次あたりが限界です。
10次、20次と次数を増やして行くには、数十万倍化ではお話しになりません。
数億倍化、数京倍化さらにずっと上の倍化を実現しないと無理です。
魔方陣は、次数が1つ上がるだけで難度が勢いよく上がってしまうからです。

さて、そのちょっとした工夫とは
    for(i=1;i<n*n+1;i++){
      h=1;
      m[y[g]][x[g]]=i
の部分に施します。
現在、各セルに

4
** ** ** **
** ** ** ** **
** ** ** ** **
** ** ** ** **
** ** ** ** **


4
** ** ** **
** ** ** ** **
** ** ** ** **
** ** ** ** **
** ** ** ** **


4
** ** ** **
** ** ** ** **
** ** ** ** **
** ** ** ** **
** ** ** ** **






・・・











1→2→3→4→5→6→7→8→9→10→11→12→13→14→15→16→17→18→19→20→21→22→23→24→25
と1から入れていっていますが、ここは1から25までの数字が漏れなく重複なしに動けばよいので、
始まりはランダムでよいわけです。例えば、
11→12→13→14→15→16→17→18→19→20→21→22→23→24→25→1→2→3→4→5→6→7→8→9→10
の動きでもよいのです。
各セルごとに始まりをランダムにする、というのが先の工夫の内容です。
さらに、1ずつ動いていますが、これも1である必要はありません。例えば、下のように動いてもよいのです。
8→15→22→4→11→18→25→7→14→21→3→10→17→24→6→13→20→2→9→16→23→5→12→19→1
1から25まで漏れなく重複なしに入っていることを確認してください。
上の動きはランダムに見えるかもしれませんが、実は規則的に動いています。
7ずつ足していっているのです。
ただし、25を超える場合は25で割った余りを取っています。
この数字は、5次魔方陣の場合、1から25の中で、5,10,15,20,25を除いた数を自由に選べます。
選べる数は25と互いに素な数です。
互いに素とは、公約数が1しかない状態をいいます。
(25,4)も互いに素です。しかし、(25,15)は5という公約数がありますので互いに素ではありまえん。
何故、互いに素でない数は選べないのでしょうか。それは下の動きを見ればわかります。
7→22→12→2→17→7→22→12→2→17→7→22→12→2→17→7→22→12→2→17→7→22→12→2→17
重複してしまっています。ということは漏れもたくさんあるということです。
1,3,4,5,・・・などが漏れてしまっています。
互いに素でないと必ず重複があって漏れがあります。
その他の数字でも是非確認してください。
互いに素な数は、n次魔方陣のnによって異なってきます。
4次魔方陣なら、4×4と互いの素になる数は1,3,5,7,9,11,13,15です。
次数によって異なるので、対応はif文によることになります。

さらに、動き自体をランダムにすることもできます。
しかし、私の実験では2重ランダム(始まりと動きをランダムにする)にしても効果はありませんでした。
2重ランダムにする方が、速いというのが私の直観なのですが。

さて、では
7→22→12→2→17→7→22→12→2→17→7→22→12→2→17→7→22→12→2→17→7→22→12→2→17
のような動きを実現するにはどうしたらよいでしょうか。

皆さんに考えていただく前にもう一つ説明が必要です。
実は、Math.random()を使用すると、毎回異なる乱数が発生します。
すると、1回1回実験結果が異なってしまいます。
ある回は1秒もかからず6次ができたのに、次の回は1次時間かけても1個もできなかったなどとなります。
そこで、毎回同じ乱数を発生させるために今回はRandom();を使います。
これの使い方は次のようにします。
Random r = new Random(500); //括弧内の数字によって乱数系列を変えることができる。乱数の種という意味でseed(シード値)といいます。
System.out.print((int)(r.nextFloat()*10)+" ");
(int)(r.nextFloat()*10)で10未満の整数が発生します。シード値はlong型です。
Random();を使うにはimport java.util.*;を冒頭に入れておく必要があります。
これヒントに考えてください。
答えは30行下。


























答え
import java.util.*;
        ・
        ・
    long s=1;
    if(n==6)s=3; //仮に3や2としてありますが、ここは実験をして速いものを選ぶので実際の値は、皆さんが研究してください。
    if(n==5)s=2;
    if(n==6)t=7; //仮に7や3としてありますが、ここは実験をして速いものを選ぶので実際の値は、皆さんが研究してください。
    if(n==5)3;    
    Random r = new Random(s);
    ii=(int)(r.nextFloat()*25);
    for(i=1;i<n*n+1;i++){
      h=1;
      iii=(t*i+ii)%(n*n)+1;



第5話へ 第7話へ

戻る

VB講義へ
VB講義基礎へ
vc++講義へ第1部へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座

初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)
初心者のための VC++による C言語 入門 C++ 入門 基礎から応用まで第1部
初心者のための VC++による C言語 入門 C++ 入門 基礎から応用まで第2部
初心者のための VC++による C言語 入門 C++ 入門 基礎から応用まで第3部