午後のひとときに、数え上げ問題を解いてみよう。
問題
n×nの領域の格子点に釘を打つ。
3つの釘に輪ゴムを掛けて、三角形を作るとする。
数学問題
3×3、4×4、5×5のそれぞれの領域において、三角形は何通り作れるでしょうか。
プログラミング問題
n×nの領域において、三角形は何通り作れるでしょうか。
nは1以上で出来るだけ大きなnについてまで求められる高速なプログラムを考えよ。
シンキングタ~イム
続いてはプログラミング編です。
このような数え上げ問題、
重要なのは、漏れなく、ダブリなく、出来る限り簡単に数えること。
数学問題の解答は、
3×3は76通り
4×4は516通り
5×5は2148通り
と、数え上げはどんどん面倒になっていく。
というわけで、手計算でやるのは面倒なので、プログラミングの出番ですね。
まずは、遅くてもいいので、純粋に数え上げるものを作ってみます。
汎用性を持たせるためにパラメータは、
引数が1つなら、nの値を指定
引数が2つなら、nの開始と終了を指定
としましょうか。
kazoeage1.c
#include <stdio.h>
#include <stdlib.h>
int gcd(int i, int j)
{
int k;
i = abs(i);
j = abs(j);
while ( j > 0 ) {
k = i%j;
i = j;
j = k;
}
return i;
}
int main(int argc, char *argv[])
{
int a,b,c;
int n,s=1,e=100;
int x1,y1,x2,y2,g;
unsigned long long count;
if ( argc == 2 ) {
s=atoi(argv[1]);
e=s;
} else if ( argc == 3 ) {
s=atoi(argv[1]);
e=atoi(argv[2]);
}
for (n=s; n<=e; n++) {
count=0LL;
for (a=0; a<n*n-2; a++)
for (b=a+1; b<n*n-1; b++)
for (c=b+1; c<n*n; c++) {
x1 = b%n-a%n;
y1 = b/n-a/n;
g = gcd(x1,y1);
x1 /= g;
y1 /= g;
x2 = c%n-a%n;
y2 = c/n-a/n;
g = gcd(x2,y2);
x2 /= g;
y2 /= g;
if ( y1 == 0 && y2 == 0 ) continue;
if ( x1 == 0 && x2 == 0 ) continue;
if ( x1 == x2 && y1 == y2 ) continue;
count++;
}
printf("%dx%d: %llu\n",n,n,count);
}
return EXIT_SUCCESS;
}
> kazoeage1 1 10
1x1: 0
2x2: 4
3x3: 76
4x4: 516
5x5: 2148
6x6: 6768
7x7: 17600
8x8: 40120
9x9: 82608
10x10: 157252
とりあえず、問題なく動作している。
gcdは、最大公約数を求めるサブルーチンです。
main内のx1やx2は負になる可能性があるので、gcdの中ではabsで正にしています。
最大公約数を求めるアルゴリズムは、ユークリッドの互除法です。
mainの中での3重ネストのa、b、cは釘の位置です。
ご丁寧に数え上げているので、countはインクリメントしかしませんので、nが大きくなるに連れ、時間がかかることが解ります。
というわけで、もう少し高速化してみます。
kazoeage2.c
#include <stdio.h>
#include <stdlib.h>
int gcd(int i, int j)
{
int k;
i = abs(i);
j = abs(j);
while ( j > 0 ) {
k = i%j;
i = j;
j = k;
}
return i;
}
int main(int argc, char *argv[])
{
int a,b;
int n,s=1,e=100;
int x1,y1,x2,y2,g,i;
unsigned long long count;
if ( argc == 2 ) {
s=atoi(argv[1]);
e=s;
} else if ( argc == 3 ) {
s=atoi(argv[1]);
e=atoi(argv[2]);
}
for (n=s; n<=e; n++) {
count = 0LL;
for (a=0; a<n*n-2; a++)
for (b=a+1; b<n*n-1; b++) {
x1 = b%n-a%n;
y1 = b/n-a/n;
g = gcd(x1,y1);
x1 /= g;
y1 /= g;
if ( y1 == 0 ) {
count += n*(n-b/n-1);
} else if ( x1 == 0 ) {
count += n-b%n-1+(n-1)*(n-b/n-1);
} else {
count += n*(n-b/n-1)+n-b%n-1;
x2 = b%n;
y2 = b/n;
i = 1;
while ( 0 <= x2+i*x1 && x2+i*x1 < n && y2+i*y1 < n ) {
i ++;
count --;
}
}
}
printf("%dx%d: %llu\n",n,n,count);
}
return EXIT_SUCCESS;
}
kazoeage1.cでは3重ループでしたが、kazoeage2.cでは2重ループにしました。
3点目は、1点目と2点目の直線上ではないところという数え方をして、一気に足し込んでいますので、多少高速化出来ていることでしょう。
> kazoeage1
1x1: 0
2x2: 4
3x3: 76
4x4: 516
5x5: 2148
6x6: 6768
7x7: 17600
8x8: 40120
9x9: 82608
10x10: 157252
11x11: 280988
12x12: 477012
13x13: 775172
14x14: 1214768
15x15: 1844512
16x16: 2725000
17x17: 3930384
18x18: 5550844
19x19: 7692300
20x20: 10482124
21x21: 14066996
22x22: 18619128
23x23: 24337056
24x24: 31449200
25x25: 40212160
26x26: 50921316
27x27: 63907468
28x28: 79542108
29x29: 98239396
30x30: 120464616
31x31: 146726096
32x32: 177595280
33x33: 213693680
34x34: 255707716
35x35: 304389180
36x36: 360557540
37x37: 425103060
38x38: 499004560
39x39: 583313520
40x40: 679169592
41x41: 787800096
42x42: 910540956
43x43: 1048811420
44x44: 1204155388
45x45: 1378211284
46x46: 1572738544
47x47: 1789625072
48x48: 2030885224
49x49: 2298647520
50x50: 2595202884
51x51: 2922970028
52x52: 3284526692
53x53: 3682600036
54x54: 4120093360
55x55: 4600053568
56x56: 5125722776
57x57: 5700510768
58x58: 6328031884
59x59: 7012089004
60x60: 7756691916
61x61: 8566023108
62x62: 9444538904
63x63: 10396883248
64x64: 11427926360
65x65: 12542789664
66x66: 13746842444
67x67: 15045691420
68x68: 16445241148
69x69: 17951623908
70x70: 19571272008
71x71: 21310880144
72x72: 23177484576
73x73: 25178354192
74x74: 27321148692
75x75: 29613797788
76x76: 32064553956
77x77: 34682026980
78x78: 37475172624
79x79: 40453282688
80x80: 43626064648
81x81: 47003522224
82x82: 50596107916
83x83: 54414655916
84x84: 58470408604
85x85: 62774948180
86x86: 67340376536
87x87: 72179199904
88x88: 77304355984
89x89: 82729217376
90x90: 88467688516
91x91: 94534035564
92x92: 100943123572
93x93: 107710265300
94x94: 114851298048
95x95: 122382573168
96x96: 130320956984
97x97: 138683822784
98x98: 147489211724
99x99: 156755619324
100x100: 166502148020
101x101: 176748480276
102x102: 187514979616
103x103: 198822497968
104x104: 210692630632
105x105: 223147488928
106x106: 236209874556
107x107: 249903326396
108x108: 264252024124
109x109: 279280717460
110x110: 295015043016
111x111: 311481180512
112x112: 328706156240
113x113: 346717633088
114x114: 365544174836
115x115: 385214947084
116x116: 405760003572
117x117: 427210145924
118x118: 449596978248
119x119: 472952983504
120x120: 497311421216
121x121: 522706303760
122x122: 549172755260
123x123: 576746640940
124x124: 605464713820
125x125: 635364623844
126x126: 666484979624
127x127: 698865211056
128x128: 732545943568
129x129: 767568546000
130x130: 803975490564
131x131: 841810143900
132x132: 881117060292
133x133: 921941562548
134x134: 964330265248
135x135: 1008330773296
136x136: 1053991657072
137x137: 1101362686640
138x138: 1150494831076
139x139: 1201439969340
140x140: 1254251343828
141x141: 1308983060804
142x142: 1365690717824
143x143: 1424430987424
144x144: 1485261710744
145x145: 1548241843408
146x146: 1613431866956
147x147: 1680893406956
148x148: 1750689277108
149x149: 1822883678212
150x150: 1897542188512
151x151: 1974731428416
152x152: 2054519773928
153x153: 2136976634416
154x154: 2222172950900
155x155: 2310181015548
156x156: 2401074666340
157x157: 2494928958180
158x158: 2591820762624
159x159: 2691828168032
160x160: 2795030792216
161x161: 2901509630224
162x162: 3011347502684
163x163: 3124628551468
164x164: 3241438777196
165x165: 3361865486292
166x166: 3485997603768
167x167: 3613925935584
168x168: 3745742849136
169x169: 3881542003264
170x170: 4021419349476
171x171: 4165472140012
172x172: 4313799537516
173x173: 4466502454564
174x174: 4623683663256
175x175: 4785447438032
176x176: 4951900047872
177x177: 5123149537808
178x178: 5299306019284
179x179: 5480481355740
180x180: 5666789355764
181x181: 5858345268756
182x182: 6055267118272
183x183: 6257674257840
184x184: 6465688385160
185x185: 6679432964352
186x186: 6899033688588
187x187: 7124618119100
188x188: 7356316071244
189x189: 7594259357460
190x190: 7838581811680
191x191: 8089419546800
192x192: 8346911060088
193x193: 8611196456512
194x194: 8882418741772
195x195: 9160722740380
196x196: 9446255394012
197x197: 9739166159684
198x198: 10039607042904
199x199: 10347731745392
200x200: 10663697056080
201x201: 10987661412176
202x202: 11319786264500
203x203: 11660235261788
204x204: 12009174419780
205x205: 12366772043428
206x206: 12733199412096
207x207: 13108630135328
208x208: 13493240184624
209x209: 13887208129984
210x210: 14290715420324
211x211: 14703945536492
212x212: 15127085578532
213x213: 15560324583492
214x214: 16003854518272
215x215: 16457870043744
216x216: 16922568486024
217x217: 17398149625392
218x218: 17884816767964
219x219: 18382775753740
220x220: 18892235180412
221x221: 19413406205892
222x222: 19946503540072
223x223: 20491744402832
224x224: 21049349381872
225x225: 21619541305680
226x226: 22202546501500
227x227: 22798594608332
228x228: 23407918123692
229x229: 24030752064980
230x230: 24667335503432
231x231: 25317909842656
232x232: 25982719934512
233x233: 26662014006240
234x234: 27356043728740
235x235: 28065063195852
236x236: 28789330649428
237x237: 29529107255284
238x238: 30284657639328
239x239: 31056249486000
240x240: 31844154504760
241x241: 32648646635456
242x242: 33470004887692
243x243: 34308510692252
244x244: 35164449238156
245x245: 36038109313604
246x246: 36929783124880
247x247: 37839766581696
248x248: 38768359377912
249x249: 39715864578928
250x250: 40682589283244
251x251: 41668843888300
252x252: 42674943155500
253x253: 43701204387460
254x254: 44747950167080
255x255: 45815506393552
256x256: 46904202370512
257x257: 48014371774800
258x258: 49146352509012
259x259: 50300485652828
260x260: 51477116838580
261x261: 52676595110644
262x262: 53899274324008
263x263: 55145512328384
264x264: 56415671014432
265x265: 57710115450912
266x266: 59029216327284
267x267: 60373347674380
268x268: 61742888330564
269x269: 63138221020276
270x270: 64559733157920
271x271: 66007815321616
272x272: 67482864243320
273x273: 68985279607648
274x274: 70515466076860
275x275: 72073833252028
276x276: 73660794499468
277x277: 75276767778180
278x278: 76922176551080
279x279: 78597448045808
280x280: 80303014027064
281x281: 82039310581152
282x282: 83806780091996
283x283: 85605868218364
284x284: 87437026328956
285x285: 89300709810996
286x286: 91197378899256
287x287: 93127499056736
288x288: 95091540904992
289x289: 97089978957824
290x290: 99123294330180
291x291: 101191971867116
292x292: 103296502201316
293x293: 105437380601060
294x294: 107615107902448
295x295: 109830188924320
296x296: 112083135051864
297x297: 114374462099088
298x298: 116704691231364
299x299: 119074349534332
300x300: 121483968803124
301x301: 123934085444580
302x302: 126425243047680
303x303: 128957989869280
304x304: 131532879455480
305x305: 134150470450288
306x306: 136811327928652
307x307: 139516021522252
308x308: 142265127986876
309x309: 145059227867924
310x310: 147898909081864
311x311: 150784764176608
312x312: 153717392490272
313x313: 156697397352224
314x314: 159725390274964
315x315: 162801987288204
316x316: 165927809570332
317x317: 169103485964324
318x318: 172329650950184
319x319: 175606944043152
320x320: 178936011662064
321x321: 182317505379632
322x322: 185752084414820
323x323: 189240412933948
324x324: 192783162111716
325x325: 196381008287764
326x326: 200034635332976
327x327: 203744733398512
328x328: 207511998519832
329x329: 211337132866336
330x330: 215220845682316
331x331: 219163851358908
332x332: 223166873332556
333x333: 227230639747028
334x334: 231355885650464
335x335: 235543353243792
336x336: 239793790934360
337x337: 244107952694560
338x338: 248486602210348
339x339: 252930507823644
340x340: 257440445463804
341x341: 262017196886084
342x342: 266661552376104
343x343: 271374307733744
344x344: 276156267251784
345x345: 281008241201184
346x346: 285931047051484
347x347: 290925510325084
348x348: 295992463286940
349x349: 301132744080372
350x350: 306347200321256
351x351: 311636684544160
352x352: 317002058269304
353x353: 322444189822512
354x354: 327963956361484
355x355: 333562240619980
356x356: 339239933934844
357x357: 344997934757044
358x358: 350837148722088
359x359: 356758490401856
360x360: 362762881551136
361x361: 368851249187936
362x362: 375024532278876
363x363: 381283675623932
364x364: 387629631458964
365x365: 394063359718692
366x366: 400585829884336
367x367: 407198018117696
368x368: 413900909786728
369x369: 420695496497072
370x370: 427582779362116
371x371: 434563767128060
372x372: 441639477405380
373x373: 448810934812916
374x374: 456079174337168
375x375: 463445237054928
376x376: 470910173390712
377x377: 478475042441120
378x378: 486140911917372
379x379: 493908856415228
380x380: 501779962234332
381x381: 509755321029556
382x382: 517836035629304
383x383: 526023216607712
384x384: 534317983492960
385x385: 542721462694528
386x386: 551234791408308
387x387: 559859116410892
388x388: 568595591931180
389x389: 577445381856052
390x390: 586409659519272
391x391: 595489604704928
392x392: 604686409819520
393x393: 614001274049888
394x394: 623435407787332
395x395: 632990029733004
396x396: 642666367617908
397x397: 652465657034484
398x398: 662389146829072
399x399: 672438092830992
400x400: 682613759714616
401x401: 692917422093024
402x402: 703350366616140
403x403: 713913886771292
404x404: 724609287075580
405x405: 735437881347268
406x406: 746400992397256
407x407: 757499954101424
408x408: 768736110427552
409x409: 780110813275920
410x410: 791625428006964
411x411: 803281326872924
412x412: 815079894006212
413x413: 827022522776068
414x414: 839110617060112
415x415: 851345589779296
416x416: 863728866344360
417x417: 876261880287824
418x418: 888946077797548
419x419: 901782913528492
420x420: 914773854662204
421x421: 927920374427412
422x422: 941223963089720
423x423: 954686117981952
424x424: 968308346889128
425x425: 982092168786384
426x426: 996039113520780
427x427: 1010150721840556
428x428: 1024428545822188
429x429: 1038874147909636
430x430: 1053489100855368
431x431: 1068274989524144
432x432: 1083233410834768
433x433: 1098365968996784
434x434: 1113674284644372
435x435: 1129159985741084
436x436: 1144824712694356
437x437: 1160670118152052
438x438: 1176697865512240
439x439: 1192909629120720
440x440: 1209307096422488
441x441: 1225891962727712
442x442: 1242665938374284
443x443: 1259630744786748
444x444: 1276788116247404
445x445: 1294139795599092
446x446: 1311687540494040
447x447: 1329433119524096
448x448: 1347378312852800
449x449: 1365524911106752
450x450: 1383874720787844
ちょっと、放おっておいたら、これくらい数えてました。
あまりやりすぎても、ブログの文字数制限にひっかかるので、これくらいにしておいてあげようw。
まだまだ高速化出来る余地は残してありますよね。
数学編でやった
全事象から余事象を引く方法です。
全事象は、(n^2)C3ですから、
(n^2)C3=(n^2)(n^2-1)(n^2-2)/6
で容易に求まりますが、
余事象は、
例えば、傾きがx=1, y=0ならば、
nC3=n(n-1)(n-2)/6
x=0, y=1も同様に、
nC3=n(n-1)(n-2)/6
例えば、傾きがx=1, y=1ならば、
Σ{m=0 to n-1} (n-m)C3+mC3
x=-1, y=1も同様に、
Σ{m=0 to n-1} (n-m)C3+mC3
といったように、
考えうる傾きを網羅させればよい。
と書いたけど、やっぱり面倒ですね。
例えば、この計算をネットで調べてみると、
ここでは、28x28まで示していますね。
また、余事象のほうでも
と38x38までしか示していません。
なんか、どっちも中途半端に少ないな。
これじゃあ、答え合わせ出来ないじゃん。
100x100の
166502148020
をググってみても、何もヒットしない。
こまったな。
ではでは
knifeのmy Pick