佳境に入ってきました。

 

 

さて、もう手作業でデータを並べるのも飽きてきましたので、しっかりとプログラミングしてみました。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(int argc, char *argv[])
{
    int x[10000],v[10000];
    int i,j,k;
    int p,q,r,t,flat;
    double s[10000],c[10000];
    double ls,lc,len1,ang1,len2,ang2;
    double acc=100000.0;
    char f[100],g[100];

    switch ( argc ) {
    default:
        printf("Usage: %s width height [flat]\n\n",argv[0]);
        printf("Calculate the order of the sides of an equiangular continuous-sided N-th-polygon.\n\n");
        printf("\tN = width * height\n");
        printf("\twidth > 1\n");
        printf("\theight > 1\n");         return EXIT_SUCCESS;
    case 4:
        flat = atoi(argv[3]);
    case 3:
        p = atoi(argv[1]);
        q = atoi(argv[2]);
    }

    sprintf(f,"\%%0%dd ",(int)(log10(p*q)+1));
    for (i=0; i<(int)(log10(p*q)+1); i++) g[i] = '#';
    g[i]=' ';
    g[i+1] = '\0';
    for (i=0; i<p*q; i++) {
        s[i] = sin(2.0*M_PI/(p*q)*i);
        c[i] = cos(2.0*M_PI/(p*q)*i);
    }

    ls = lc = 0.0;
    for (i=0; i<p*q; i+=p) {
        x[i/p] = i/p+1;
        ls += x[i/p]*s[i];
        lc += x[i/p]*c[i];
    }
    k = 0;
    for (i=0; i<q; i++) {
        v[k] = x[i];
        k++;
    }
    len1 = sqrt(ls*ls+lc*lc);
    ang1 = atan2(lc,ls);
    if ( ang1 < 0.0 ) ang1 += 2*M_PI;

    for (r=1; r<p; r++) {
        while ( k < r*q ) {
            v[k] = -1;
            k++;
        }
        t = 0;
        do {
            ls = lc = 0.0;
            for (i=0; i<p*q; i+=p) {
                ls += (x[(i/p+t)%q]+q*r)*s[i+r];
                lc += (x[(i/p+t)%q]+q*r)*c[i+r];
            }
            len2 = sqrt(ls*ls+lc*lc);
            ang2 = atan2(lc,ls);
            if ( ang2 < ang1 ) ang2 += 2*M_PI;
            if ( (int)(len1*acc+0.5) == (int)(len2*acc+0.5) ) {
                for (i=1; i<p; i++) {
                    if ( (int)((ang2-ang1)*180.0/M_PI*acc+0.5)
                      == (int)(360.0/p*i*acc+0.5) ) break;
                }
                if ( i < p ) {
                    for (i=0; i<q; i++) {
                        v[k] = x[(i+t)%q]+q*r;
                        k++;
                    }
                    t = q;
                }
            }
            t++;
        } while( t < q );
    }
    for (i=0; i<q; i++) {
        for (j=0; j<p; j++) {
            if ( v[j*q+i] > 0 ) {
                printf(f,v[j*q+i]);
            } else {
                printf(g);
            }
        }
        if ( !flat && i+1 < q ) printf("\n");
    }

    return EXIT_SUCCESS;
}

まぁ、プログラムの説明を少しだけしておく。

プログラム名は何でも良いので好きなようにつけてくれて構わない。

プログラムには、最低2つのパラメータを与えて、長方形にデータを列挙させる。
3つ目のパラメータに0以外を指定すると、フラットに列挙する。

今までの記事で書いていたp個の開いたq辺形ということで、縦に開いたq辺形の辺の長さが並ぶことになるので、最初の1列は1からQまでが昇順で並ぶこととなる。

この一連の記事において、最初は組み合わせで多重ネストしてやっていましたが、組合せ爆発によってn=16以降は解を見つけるのが困難となっていった。

しかし、n=6、10、12、14、15に解が見つかったことから、ある程度の推測や考察が進み、その内容がおぼろげに見えてきました。

Nは複数の種類の素因数を持つことが必要(十分かどうかはまだわからなかった)条件だと感じました。

また、内角は一定であることで、それぞれを仰角として、すべての仰角が出揃い、すべての辺の長さの組み合わせだけを考えれば良いという単純なことに気が付きます。

すると、先の素因数分解の意味が、n-th多角形のn=pq、つまりp個の開いた等角q辺形の組み合わせで、それぞれの閉じていない部分はq個のベクトルの和であり、ベクトルの絶対値が等しく、それらが複素数平面の原点からの偏角の違いで相殺されて、結果的にn-th多角形になると原点に戻ってくるという仕組みであると考えるようになる。

つまり、q個のベクトルの和は、絶対値と偏角θであり、q本のベクトルの絶対値が等しく、偏角θが360˚を等間隔にp分割されていれば良いということだけの話しだと考えたわけです。

つまり、それ以外には解が存在しないという考えで推し進めるに至ったわけです。

例えば、n=7で、辺の順番を1, 4, 7, 2, 3, 5, 6とすると、始点と終点はよっぽど大きく描かないと、ほとんど同じ座標になってしまう。
これは、当初のプログラムでは始点と終点の距離が近いことに重きを置いていたので、計算誤差でその差が出てしまうということで選ばれたデータだったわけです。
これは数学的に考えると全く意味のないものである。
つまり、誤差に振り回されていたとも言えなくもない。

辺の長さは1~nまであるが、これを昇順のままp個ずつq組に分ける。
1組目をそのままで、絶対値、偏角が求まるので、残りの組は順番をスライドするだけで、組み合わせを試さずに、単純に絶対値が等しいものを探し、更に偏角として該当するものを探すということで、今までとは全く違ったアプローチのプログラムとして完成したと言える。

ただ、これは解が存在するしないを探索するものではないので、いままで積み上げてきた法則に外れるような例外があると、それに対応出来るわけではない。

組み合わせによる検索では、何ヶ月、何年も掛かる計算を、単純なデータの存在だけを瞬殺してしまうという意味では、ある意味驚異的なプログラムではある。
何通りあるとか、そういうことは、後から代数的にでも計算すれば良いだろう。

なんて、自画自賛してもしかたがないが、やっとこの問題から開放されたとも言える。

強いてやり残しがあるとするならば、昇順だけではなくて、降順のデータも試すということをやっても良いかもしれないが、おそらくは冗長だと思う。

あとは例外が無いということが、数学的に証明出来れば良いのだが、その辺はいずれやる必要はあるのかもしれないが、それはそれで。


ではでは