英字+記号キー30個の並べ替え可能数はいくつなのか……

 キーボード上にある英字をキー入れ替えソフトでキー後と入れ替えた場合、どういう風に文字を左右に割り振れば交互打鍵率が最も高くなるか?を探してみるつもりでいたり。
 (2005年12月31日22:16:05追記 : NICOLAとかを並べ替える方法論に書いたまま、三段階(全範囲内・交互打鍵高く→各手範囲内・同手跳躍低く→各手各段範囲内・アルペジオ率高く)に分けて解析する予定です。)


 キー範囲は

QWERTYUIOP
ASDFGHJKL;
ZXCVBNM<>/

だけを入れ替える方向で。
 ……まずは、どういう組み合わせ・いくつの組み合わせ数がありえるのかを調べねば。


 以降は、数学が非常に苦手だったので、適当に計算(いいのかそれで)。

30もじ
ランダムに15個選ぶ→これをDに割り当てる。
残り15個はKに割り当てる。
【どうでもいい】スペースキーを挟む前後は同手の方が良い。


2文字
ランダムに1個選ぶ→これをDに割り当てる。
残り1個はDに割り当てる。
a b 1
b a 1


4文字
ランダムに二個選ぶ→これをDに割り当てる。
残り二個はDに割り当てる。
ab cd 1
ac bd 2
ad bc 3
bc ad 3
bd ac 2
cd ab 1
左右が同じならば3パタン。


6文字
ランダムに3こ→D
のこりもの→K
abc def 1
abd cef 2
abe cdf 3
abf cde 4
acd bef 5
ace bdf 6
acf bde 7
ade bcf 8
adf bce 9
aef bcd 10
bcd aef 10
bce adf 9
bcf ade 8
bde acf 7
bdf ace 6
bef acd 5
cde abf 4
cdf abe 3
cef abd 2
def abc 1

キー数
2 / 2 = 1 … 1
4 / 2 = 2 … 3
6 / 2 = 3 … 10
8 / 2 = 4 … 33
10 / 2 = 5 … 100
12 / 2 = 6 … 333
14 / 2 = 7 … 1,000
16 / 2 = 8 … 3,333
18 / 2 = 9 … 10,000
20 / 2 = 10 … 33,333
22 / 2 = 11 … 100,000
24 / 2 = 12 … 333,333
26 / 2 = 13 … 1,000,000
28 / 2 = 14 … 3,333,333
30 / 2 = 15 … 10,000,000


 で、適当に類推すると片手に15キー割り当てた場合に取りうるパタン数は……1000万通り?
 いや、そもそもなんか変なんだよな……。
 考え直してきますorz

試験スクリプト( for jgawk )。

 2進数30ビットの数を順に出して、それぞれについて1の個数を数えて表示するだけ。

sum=0 000000000000000000000000000000
sum=1 000000000000000000000000000001
sum=1 000000000000000000000000000010
sum=2 000000000000000000000000000011
sum=1 000000000000000000000000000100
sum=2 000000000000000000000000000101
sum=2 000000000000000000000000000110
sum=3 000000000000000000000000000111
sum=1 000000000000000000000000001000
sum=2 000000000000000000000000001001
sum=2 000000000000000000000000001010
sum=3 000000000000000000000000001011
sum=2 000000000000000000000000001100
sum=3 000000000000000000000000001101
sum=3 000000000000000000000000001110
sum=4 000000000000000000000000001111
sum=1 000000000000000000000000010000


こんな感じの表示をするだけ。

BEGIN{

	/* 左手領域 */
	
	key[1] = 0 ;
	key[2] = 0 ;
	key[3] = 0 ;
	key[4] = 0 ;
	key[5] = 0 ;

	key[6] = 0 ;
	key[7] = 0 ;
	key[8] = 0 ;
	key[9] = 0 ;
	key[10] = 0 ;

	key[11] = 0 ;
	key[12] = 0 ;
	key[13] = 0 ;
	key[14] = 0 ;
	key[15] = 0 ;


	/* 右手領域 */

	key[16] = 0 ;
	key[17] = 0 ;
	key[18] = 0 ;
	key[19] = 0 ;
	key[20] = 0 ;

	key[21] = 0 ;
	key[22] = 0 ;
	key[23] = 0 ;
	key[24] = 0 ;
	key[25] = 0 ;

	key[26] = 0 ;
	key[27] = 0 ;
	key[28] = 0 ;
	key[29] = 0 ;
	key[30] = 0 ;


	for(i=1;total<=3;i++){
		_total = key[30] + key[29] + key[28] + key[27] + key[26] + key[25] + key[24] + key[23] + key[22] + key[21] + key[20] + key[19] + key[18] + key[17] + key[16] + key[15] + key[14] + key[13] + key[12] + key[11] + key[10] + key[9] + key[8] + key[7] + key[6] + key[5] + key[4] + key[3] + key[2] + key[1] ;
		printf( "sum=%s " ,  _total );

		/* 値を表示 */
		for(j=30;j>=1;j--){
			printf( "%s" ,  key[j] );
		}
		printf( "\n" );

			/* 加算 */
			key[1] = key[1] + 1;
			/* print key[1]; */

			/* 桁繰り上げ */
		for (j=1;j<=30;j++){
			if(key[j]>=2){
				key[j] = 0;
				key[j+1] = key[j+1] + 1;
			}
		}

			
	}


}

「1」が30bit中15個だけ立つものを表示するスクリプト

 あまりにも時間がかかりすぎるので、「000000000000000111111111111111」からスタートすることにしました。
 (2005年12月29日15:56:58に実行開始)


 ついでにいうと、ホントは上位ビットの計算を(まだ計算する可能性がない部分についてだけでも)計算をはしょると良いはず……でもどうやるんだろう?とりあえずはそのまま走らせてテストしています。
 何時間ぐらいで結果が出るのかランニングテストしてから、ファイルに書き出してみようかと。


 あっ、上位ビットが15個1になる「111111111111111000000000000000」以降についても、計算するだけ無駄なんだな……後で終了条件を変更しないと。


 こんな感じでとりあえず。




sum=15 000001000011000111111110110110
sum=15 000001000011000111111110111001
sum=15 000001000011000111111110111010
sum=15 000001000011000111111110111100
sum=15 000001000011000111111111000111
sum=15 000001000011000111111111001011
sum=15 000001000011000111111111001101
sum=15 000001000011000111111111001110
sum=15 000001000011000111111111010011
sum=15 000001000011000111111111010101
sum=15 000001000011000111111111010110
sum=15 000001000011000111111111011001
sum=15 000001000011000111111111011010
sum=15 000001000011000111111111011100
sum=15 000001000011000111111111100011
sum=15 000001000011000111111111100101


 範囲内にある30キーを左右どちらの手に割り当てるか?という問いがあるとして、その「振り分け可能な全ての手」とその個数を出すのに使えます。


 ……が、それができても単に「計算する必要がある手を確定したのみ」であって、手数そのものはまだまだ膨大なんですよね。
 「かな」における法則のように、英文にも相応の法則があるはず……そういった方法を使って予め手数を狭めておかないと、実際の最適化計算は結構大変かもしれませんね……むむむ。

BEGIN{

	/* 左手領域 */
	
	key[1] = 1 ;
	key[2] = 1 ;
	key[3] = 1 ;
	key[4] = 1 ;
	key[5] = 1 ;

	key[6] = 1 ;
	key[7] = 1 ;
	key[8] = 1 ;
	key[9] = 1 ;
	key[10] = 1 ;

	key[11] = 1 ;
	key[12] = 1 ;
	key[13] = 1 ;
	key[14] = 1 ;
	key[15] = 1 ;


	/* 右手領域 */

	key[16] = 0 ;
	key[17] = 0 ;
	key[18] = 0 ;
	key[19] = 0 ;
	key[20] = 0 ;

	key[21] = 0 ;
	key[22] = 0 ;
	key[23] = 0 ;
	key[24] = 0 ;
	key[25] = 0 ;

	key[26] = 0 ;
	key[27] = 0 ;
	key[28] = 0 ;
	key[29] = 0 ;
	key[30] = 0 ;


	while(total +1 <= 30){
		total = key[30] + key[29] + key[28] + key[27] + key[26] + key[25] + key[24] + key[23] + key[22] + key[21] + key[20] + key[19] + key[18] + key[17] + key[16] + key[15] + key[14] + key[13] + key[12] + key[11] + key[10] + key[9] + key[8] + key[7] + key[6] + key[5] + key[4] + key[3] + key[2] + key[1] ;
		/* printf( "sum=%s " ,  total ); */

		/* 値を表示 */
		if ( total == 15 ){
			printf( "sum=%s " ,  total );
			for(j=30;j>=1;j--){
				printf( "%s" ,  key[j] );
			};
			printf( "\n" );
		};

			/* 加算 */
			key[1] = key[1] + 1;
			/* print key[1]; */

			/* 桁繰り上げ */
		for (j=1;j<=30;j++){
			if(key[j]>=2){
				key[j] = 0;
				key[j+1] = key[j+1] + 1;
			}
		}

			
	}


}

 2005年12月29日19:02:54の時点で計算中止、とにかく時間がかかりすぎです……orz

終了条件も決めてみた。


 「000000000000000111111111111111」〜「111111111111111000000000000000」までの間だけを計算するように改めてみました。

BEGIN{

	/* 左手領域 */
	
	key[1] = 1 ;
	key[2] = 1 ;
	key[3] = 1 ;
	key[4] = 1 ;
	key[5] = 1 ;

	key[6] = 1 ;
	key[7] = 1 ;
	key[8] = 1 ;
	key[9] = 1 ;
	key[10] = 1 ;

	key[11] = 1 ;
	key[12] = 1 ;
	key[13] = 1 ;
	key[14] = 1 ;
	key[15] = 1 ;


	/* 右手領域 */

	key[16] = 0 ;
	key[17] = 0 ;
	key[18] = 0 ;
	key[19] = 0 ;
	key[20] = 0 ;

	key[21] = 0 ;
	key[22] = 0 ;
	key[23] = 0 ;
	key[24] = 0 ;
	key[25] = 0 ;

	key[26] = 0 ;
	key[27] = 0 ;
	key[28] = 0 ;
	key[29] = 0 ;
	key[30] = 0 ;


	while( UpperSideBit != 15){
		total = key[30] + key[29] + key[28] + key[27] + key[26] + key[25] + key[24] + key[23] + key[22] + key[21] + key[20] + key[19] + key[18] + key[17] + key[16] + key[15] + key[14] + key[13] + key[12] + key[11] + key[10] + key[9] + key[8] + key[7] + key[6] + key[5] + key[4] + key[3] + key[2] + key[1] ;
		/* printf( "sum=%s " ,  total ); */

		/* 値を表示 */
		if ( total == 15 ){
			printf( "sum=%s " ,  total );
			for(j=30;j>=1;j--){
				printf( "%s" ,  key[j] );
			};
			printf( "\n" );
		};

			/* 加算 */
			key[1] = key[1] + 1;
			/* print key[1]; */

			/* 桁繰り上げ */
		for (j=1;j<=30;j++){
			if(key[j]>=2){
				key[j] = 0;
				key[j+1] = key[j+1] + 1;
			}
		}

		/* 終了条件の算出 */
		UpperSideBit = key[30] + key[29] + key[28] + key[27] + key[26] + key[25] + key[24] + key[23] + key[22] + key[21] + key[20] + key[19] + key[18] + key[17] + key[16] ;
		/* print UpperSideBit; */
		
	}


}

ちょっと解りにくいなぁ……と思ったので、

 ゼロとイチ以外の表示方法を追加してみたり。

・
・
・
sum=15 000001001000111011111111100001
xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;
sum=15 右右右右右左右右左右右右左左左右左左左左左左左左左右右右右左

xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;
sum=15 000001001000111011111111100010
xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;
sum=15 右右右右右左右右左右右右左左左右左左左左左左左左左右右右左右

xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;
sum=15 000001001000111011111111100100
xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;
sum=15 右右右右右左右右左右右右左左左右左左左左左左左左左右右左右右

xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;
sum=15 000001001000111011111111101000
xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;
sum=15 右右右右右左右右左右右右左左左右左左左左左左左左左右左右右右

xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;
sum=15 000001001000111011111111110000
xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;
sum=15 右右右右右左右右左右右右左左左右左左左左左左左左左左右右右右
・
・
・
BEGIN{

	print "0は左手、1は右手だと思ってください……";
	print "(注:重複チェックはしていないので、【右:AB 左:CD】と【右:CD 左:AB】は別々とみなされます……左右同数なのでコレを考慮すれば全体を半数に圧縮できるはずです)";
	print "";


	/* 左手領域 */
	
	key[1] = 1 ;
	key[2] = 1 ;
	key[3] = 1 ;
	key[4] = 1 ;
	key[5] = 1 ;

	key[6] = 1 ;
	key[7] = 1 ;
	key[8] = 1 ;
	key[9] = 1 ;
	key[10] = 1 ;

	key[11] = 1 ;
	key[12] = 1 ;
	key[13] = 1 ;
	key[14] = 1 ;
	key[15] = 1 ;


	/* 右手領域 */

	key[16] = 0 ;
	key[17] = 0 ;
	key[18] = 0 ;
	key[19] = 0 ;
	key[20] = 0 ;

	key[21] = 0 ;
	key[22] = 0 ;
	key[23] = 0 ;
	key[24] = 0 ;
	key[25] = 0 ;

	key[26] = 0 ;
	key[27] = 0 ;
	key[28] = 0 ;
	key[29] = 0 ;
	key[30] = 0 ;


	while( UpperSideBit != 15){
		total = key[30] + key[29] + key[28] + key[27] + key[26] + key[25] + key[24] + key[23] + key[22] + key[21] + key[20] + key[19] + key[18] + key[17] + key[16] + key[15] + key[14] + key[13] + key[12] + key[11] + key[10] + key[9] + key[8] + key[7] + key[6] + key[5] + key[4] + key[3] + key[2] + key[1] ;
		/* printf( "sum=%s " ,  total ); */

		/* 値を表示 */
		if ( total == 15 ){
			print "xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;";
			printf( "sum=%s " ,  total );
			for(j=30;j>=1;j--){
				printf( "%s" ,  key[j] );
			};
			printf( "\n" );
		};
		/* 【左】【右】で表示 */
		if ( total == 15 ){
			print "xxx=xx ABCDEFGHIJKLMNOPQRSTUVWXYZ,./;";
			printf( "sum=%s " ,  total );
			for(j=30;j>=1;j--){
				if (key[j] == 1){printf( "左" )};
				if (key[j] == 0){printf( "右" )};
			};
			printf( "\n\n" );
		};

			/* 加算 */
			key[1] = key[1] + 1;
			/* print key[1]; */

			/* 桁繰り上げ */
		for (j=1;j<=30;j++){
			if(key[j]>=2){
				key[j] = 0;
				key[j+1] = key[j+1] + 1;
			}
		}

		/* 終了条件の算出 */
		UpperSideBit = key[30] + key[29] + key[28] + key[27] + key[26] + key[25] + key[24] + key[23] + key[22] + key[21] + key[20] + key[19] + key[18] + key[17] + key[16] ;
		/* print UpperSideBit; */
		
	}


}