Optimal Golomb Rulerを探す

2003/3/4更新

Optimal Golomb Ruler(OGR)とは、Golomb Rulerの内もっとも短いものの事です。Golomb Rulerとはn個の数字からなる数列GR-nにおいて、任意の要素a,b,c,d(ただしa>b、c>d、a≠b、c≠d)がa-b≠c-dを満たすものを言います。nの事をマーク数と呼ぶこともあります。

GR-nは全ての要素を自然数倍しても関係が壊れないため、無限に存在します。その内、数列の要素の最大値が最も小さいものをOGR-nとします。GRは反転しても関係が壊れないため、OGR-nは常に偶数個存在することになります。

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

int check_ruler( int mark_count, int *ruler );
void print_ruler( int mark_count, int *ruler );


int main( int ac, char *av[] )
{
    int mark, mark_count, *ruler, length, found, nodes;

    /* コマンドラインからマーク数を決定する */
    if( ac < 2 )
        return;
    mark_count = strtol( av[1], NULL, 10 );
    if( mark_count < 2 )
        mark_count = 2;

    /* ruler用配列を確保する */
    ruler = calloc( mark_count, sizeof(int) );
    if( ruler == NULL )
        return;

    /* ループ変数を初期化する */
    length = mark_count - 1;
    found = 0;
    nodes = 0;

    /* OGR探索ループ */
    while( found == 0 ) {
        /* rulerを0,1,2...,lengthで初期化する */
        for( mark = 1; mark < mark_count - 1; mark++ )
            ruler[mark] = mark;
        ruler[mark_count - 1] = length;

        /* 長さがlengthのGRを探す */
        while( 1 ) {
            /* 計算回数をカウント */
            nodes++;

            /* golomb rulerであるか */
            if( check_ruler( mark_count, ruler ) == 1 ) {
                print_ruler( mark_count, ruler );
                found = 1;
            }

            /* markを一つ先に進める */
            for( mark = mark_count - 2; mark > 0; mark-- ) {
                if( ruler[mark] < length - ( mark_count - mark - 1 ) ) {
                    ruler[mark]++;
                    for( mark = mark + 1; mark < mark_count - 1; mark++ )
                        ruler[mark] = ruler[mark - 1] + 1;
                    break;
                }
            }
            /* 進めるマークが無ければ終了 */
            if( mark == 0 )
                break;
        }
        /* lengthを増やす */
        length++;
    }

    /* 計算回数を表示 */
    printf( "%d nodes\n", nodes );
}


/* golomb ruler判定 */
int check_ruler( int mark_count, int *ruler )
{
    int n1, n2, n3, n4;

    for( n1 = 0; n1 < mark_count - 1; n1++ ) {
        for( n2 = n1 + 1; n2 < mark_count; n2++ ) {
            for( n3 = 0; n3 < mark_count - 1; n3++ ) {
                if( n1 == n3 )
                    continue;
                for( n4 = n3 + 1; n4 < mark_count; n4++ ) {
                    if( n2 == n4 )
                        continue;
                    /* 差の等しい要素の組があれば偽 */
                    if( ruler[n2] - ruler[n1] == ruler[n4] - ruler[n3] )
                        return( 0 );
                }
            }
        }
    }

    return( 1 );
}


/* ruler表示 */
void print_ruler( int mark_count, int *ruler )
{
    int mark;

    for( mark = 0; mark < mark_count; mark++ ) {
        printf( "%d", ruler[mark] );
        if( mark != mark_count - 1 )
            printf( "-" );
    }
    printf( "\n" );
}

定義に従い、Rulerを短いものから順に長くしていきながら全ての数列をGRとなるか試しています。当然ながらマーク数が多くなるほど計算に時間がかかり、現在のところOGR-11までしか求められていません。その結果は以下のとおりです。

OGR-4 0-1-4-6
0-2-5-6
OGR-5 0-1-4-9-11
0-2-7-8-11
0-2-7-10-11
0-3-4-9-11
OGR-6 0-1-4-10-12-17
0-1-4-10-15-17
0-1-8-11-13-17
0-1-8-12-14-17
0-2-7-13-16-17
0-3-5-9-16-17
0-4-6-9-16-17
0-5-7-13-16-17
OGR-7 0-1-4-10-18-23-25
0-1-7-11-20-23-25
0-1-11-16-19-23-25
0-2-3-10-16-21-25
0-2-5-14-18-24-25
0-2-6-9-14-24-25
0-2-7-13-21-22-25
0-2-7-15-21-24-25
0-3-4-12-18-23-25
0-4-9-15-22-23-25
OGR-8 0-1-4-9-15-22-32-34
0-2-12-19-25-30-33-34
OGR-9 0-1-5-12-25-27-35-41-44
0-3-9-17-19-32-39-43-44
OGR-10 0-1-6-10-23-26-34-41-53-55
0-2-14-21-29-32-45-49-54-55
OGR-11 0-1-4-13-28-33-47-54-64-70-72
0-1-9-19-24-31-52-56-58-69-72
0-2-8-18-25-39-44-59-68-71-72
0-3-14-16-20-41-48-53-63-71-72

世界では、OGR-23まで既に求められているそうです。また、OGRでは無いかもしれないが、ほぼOGRと思われる近似解はGR-150まで求められているそうです。