回答編集履歴

2

コードの効率化修正で追記

2020/04/23 10:57

投稿

syalpon
syalpon

スコア24

test CHANGED
@@ -283,3 +283,167 @@
283
283
  (N-1)!! = (N-1)*(N-3)*(N-5)...*1 個あります。([二重階乗](https://ja.wikipedia.org/wiki/%E4%BA%8C%E9%87%8D%E9%9A%8E%E4%B9%97))
284
284
 
285
285
  このように階乗で増えていくのでN=14くらいから再帰が深くなりすぎて最後までは辿りつかないようです。
286
+
287
+
288
+
289
+
290
+
291
+ **ここより先追記。**
292
+
293
+ 二重階乗であることを使えば再帰しなくても済んだので効率的に求めることができました。
294
+
295
+ N=200で1億回目でも簡単に求まります。
296
+
297
+ 自分が試したところN=10000000程度でも問題はなかったです。
298
+
299
+ ```C
300
+
301
+ #include <stdio.h>
302
+
303
+ #define N 200
304
+
305
+
306
+
307
+ void show(int *,int ,int );
308
+
309
+
310
+
311
+ int main(void){
312
+
313
+ int i,k,n,index,count=0;
314
+
315
+ // S = {0,0,0,...,0,-1}
316
+
317
+ int S[N/2] = {0};
318
+
319
+ S[N/2-1] = -1;
320
+
321
+
322
+
323
+ //入力
324
+
325
+ printf("繰返し回数->");
326
+
327
+ scanf("%d",&k);
328
+
329
+ printf("要素の番号->");
330
+
331
+ scanf("%d",&n);
332
+
333
+ //昇順列の二つずつ各ペアに置換の求める
334
+
335
+ while(count<k-1){
336
+
337
+ index = 0;
338
+
339
+ count++;
340
+
341
+ //毎回表示するならこれを実行
342
+
343
+ //show(S,count,n);
344
+
345
+
346
+
347
+ S[index]++ ;
348
+
349
+ // 繰り上がり
350
+
351
+ while(S[index] >= N-1-2*index){
352
+
353
+ S[index] = 0;
354
+
355
+ S[index+1]++;
356
+
357
+ index++;
358
+
359
+ }
360
+
361
+ //全部調べたら終了
362
+
363
+ //配列最後の-1は表示時の終了条件で使うので代入
364
+
365
+ if(S[N/2-1] != -1){
366
+
367
+ S[N/2-1] = -1;
368
+
369
+ break;
370
+
371
+ }
372
+
373
+ }
374
+
375
+
376
+
377
+ //表示
378
+
379
+ show(S,k,n);
380
+
381
+ return 0;
382
+
383
+ }
384
+
385
+
386
+
387
+ void show(int *S,int k,int n){
388
+
389
+ int i,temp,index=0;
390
+
391
+ // A[] = {1,2,3,4,...,N}
392
+
393
+ int A[N];
394
+
395
+ for(i=0;i<N;i++){A[i] = i+1;}
396
+
397
+ while(*S != -1){
398
+
399
+ /* Sの置換表現通りに入れ替え */
400
+
401
+ temp = A[2*index+1];
402
+
403
+ A[2*index+1] = A[ 2*index+1+(*S)];
404
+
405
+ A[2*index+1+(*S)] = temp;
406
+
407
+ //次の置換へ
408
+
409
+ index++;
410
+
411
+ S++;
412
+
413
+ }
414
+
415
+
416
+
417
+ printf("\n%d回目: ",k);
418
+
419
+ for(i=0;i<N;i+=2){printf("(%d,%d) ",A[i],A[i+1]);}
420
+
421
+ for(i=0;i<N;i+=2){
422
+
423
+ if( A[i]==n ){printf("\n%dのペアは%dです。\n",n,A[i+1]);break;}
424
+
425
+ if(A[i+1]==n){printf("\n%dのペアは%dです。\n",n, A[i] );break;}
426
+
427
+ if(i==N-2){printf("%dのペアはありません\n",n);}
428
+
429
+ }
430
+
431
+
432
+
433
+ }
434
+
435
+ ```
436
+
437
+
438
+
439
+ 少し数学チックに解いた部分はあるのですが、アルゴリズム的にはN=6の場合
440
+
441
+ (12)(34)(56)の一つ目の(12)の部分をみて左の数値を固定して、右の数値を自分より後の数値と入れ替えることを考えます。(”後”とは入れ替えないような自分自身と入れ替える場合も含みます)
442
+
443
+ (1[2])(34)(56)だから2の移動先は2,3,4,5,6の5通りで入れ替えた後はそれより右の組だけを考えます。たとえばこの例で2が5と入れ替わったとしましょう。すると
444
+
445
+ (15)(34)(26)となり、ここで(15)は固定して残りの(34)(26)で同じことを繰り返すことによって同じパターンが出ずに全てを網羅できます。
446
+
447
+ ここで入れ替えた所をそれぞれ自分自身からの距離(2と5を入れ替えるのであれば3)を配列にして格納することで再帰することなく、求めることができます。
448
+
449
+ ちなみN=6の場合だと2の移動先が5通り、その次の右側の数字の移動先が3通り、その次の右側の数字が1通りなので全部で15通りと、ちゃんとN!!通りになっていることが確かめられます。

1

誤字脱字

2020/04/23 10:57

投稿

syalpon
syalpon

スコア24

test CHANGED
@@ -270,7 +270,7 @@
270
270
 
271
271
  1. (1,2)(3,4)
272
272
 
273
- 2. (1,3)(3,4)
273
+ 2. (1,3)(2,4)
274
274
 
275
275
  3. (1,4)(2,3)
276
276
 
@@ -278,8 +278,8 @@
278
278
 
279
279
  とペアの組は三種類あり、N=6のときであれば15種類あります。
280
280
 
281
- このペアというのは、辺のない正N角形で各頂点から1本ずつ線を引いている図形と対応できます。
281
+ このペアというのは、辺のない正N角形で各頂点から1本ずつ線を引いている図形と対応できるので
282
-
282
+
283
- になっていて(N-1)!! = (N-1)*(N-3)*(N-5)...*1 個あります。([二重階乗](https://ja.wikipedia.org/wiki/%E4%BA%8C%E9%87%8D%E9%9A%8E%E4%B9%97))
283
+ (N-1)!! = (N-1)*(N-3)*(N-5)...*1 個あります。([二重階乗](https://ja.wikipedia.org/wiki/%E4%BA%8C%E9%87%8D%E9%9A%8E%E4%B9%97))
284
284
 
285
285
  このように階乗で増えていくのでN=14くらいから再帰が深くなりすぎて最後までは辿りつかないようです。