for文マンセー

朝、出かける前にレクリエーション数学の本をぱらっと見たら、3辺のうちいずれか1本の長さが48となる直角三角形は10個ある、というようなことが書いてあった。詳細は見なかったけど、よほど多いということなんだろうか。

Haskellの例題として、これを検証してみようと思った。で、こういうのは先に答えを知ってるほうがいいだろうと思って、ひとまずCで計算してみた。

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

int main() {
  for (int x = 1; x < 200; x++) { 
    printf ("-----(%3d)------\n", x);
    for (int i = 1; i < 40000; i++) {
      for (int j = 1; j < 40000; j++) {
	if (i*i == x*x + j*j) {
	  printf ("%5d^2  + %5d^2 = %6d^2\n", j, x, i);
	}
      }
    }
  }
  return 0;
}

結果のうち48近辺を引用すると:

-----( 29)------
  420^2  +    29^2 =    421^2
-----( 30)------
   16^2  +    30^2 =     34^2
   40^2  +    30^2 =     50^2
   72^2  +    30^2 =     78^2
  224^2  +    30^2 =    226^2
-----( 31)------
  480^2  +    31^2 =    481^2
-----( 32)------
   24^2  +    32^2 =     40^2
   60^2  +    32^2 =     68^2
  126^2  +    32^2 =    130^2
  255^2  +    32^2 =    257^2
-----( 33)------
   44^2  +    33^2 =     55^2
   56^2  +    33^2 =     65^2
  180^2  +    33^2 =    183^2
  544^2  +    33^2 =    545^2
-----( 34)------
  288^2  +    34^2 =    290^2
-----( 35)------
   12^2  +    35^2 =     37^2
   84^2  +    35^2 =     91^2
  120^2  +    35^2 =    125^2
  612^2  +    35^2 =    613^2
-----( 36)------
   15^2  +    36^2 =     39^2
   27^2  +    36^2 =     45^2
   48^2  +    36^2 =     60^2
   77^2  +    36^2 =     85^2
  105^2  +    36^2 =    111^2
  160^2  +    36^2 =    164^2
  323^2  +    36^2 =    325^2
-----( 37)------
  684^2  +    37^2 =    685^2
-----( 38)------
  360^2  +    38^2 =    362^2
-----( 39)------
   52^2  +    39^2 =     65^2
   80^2  +    39^2 =     89^2
  252^2  +    39^2 =    255^2
  760^2  +    39^2 =    761^2
-----( 40)------
    9^2  +    40^2 =     41^2
   30^2  +    40^2 =     50^2
   42^2  +    40^2 =     58^2
   75^2  +    40^2 =     85^2
   96^2  +    40^2 =    104^2
  198^2  +    40^2 =    202^2
  399^2  +    40^2 =    401^2
-----( 41)------
  840^2  +    41^2 =    841^2
-----( 42)------
   40^2  +    42^2 =     58^2
   56^2  +    42^2 =     70^2
  144^2  +    42^2 =    150^2
  440^2  +    42^2 =    442^2
-----( 43)------
  924^2  +    43^2 =    925^2
-----( 44)------
   33^2  +    44^2 =     55^2
  117^2  +    44^2 =    125^2
  240^2  +    44^2 =    244^2
  483^2  +    44^2 =    485^2
-----( 45)------
   24^2  +    45^2 =     51^2
   28^2  +    45^2 =     53^2
   60^2  +    45^2 =     75^2
  108^2  +    45^2 =    117^2
  200^2  +    45^2 =    205^2
  336^2  +    45^2 =    339^2
 1012^2  +    45^2 =   1013^2
-----( 46)------
  528^2  +    46^2 =    530^2
-----( 47)------
 1104^2  +    47^2 =   1105^2
-----( 48)------
   14^2  +    48^2 =     50^2
   20^2  +    48^2 =     52^2
   36^2  +    48^2 =     60^2
   55^2  +    48^2 =     73^2
   64^2  +    48^2 =     80^2
   90^2  +    48^2 =    102^2
  140^2  +    48^2 =    148^2
  189^2  +    48^2 =    195^2
  286^2  +    48^2 =    290^2
  575^2  +    48^2 =    577^2
-----( 49)------
  168^2  +    49^2 =    175^2
 1200^2  +    49^2 =   1201^2
-----( 50)------
  120^2  +    50^2 =    130^2
  624^2  +    50^2 =    626^2
-----( 51)------
   68^2  +    51^2 =     85^2
  140^2  +    51^2 =    149^2
  432^2  +    51^2 =    435^2
 1300^2  +    51^2 =   1301^2
-----( 52)------
   39^2  +    52^2 =     65^2
  165^2  +    52^2 =    173^2
  336^2  +    52^2 =    340^2
  675^2  +    52^2 =    677^2
-----( 53)------
 1404^2  +    53^2 =   1405^2
-----( 54)------
   72^2  +    54^2 =     90^2
  240^2  +    54^2 =    246^2
  728^2  +    54^2 =    730^2
-----( 55)------
   48^2  +    55^2 =     73^2
  132^2  +    55^2 =    143^2
  300^2  +    55^2 =    305^2
 1512^2  +    55^2 =   1513^2
-----( 56)------
   33^2  +    56^2 =     65^2
   42^2  +    56^2 =     70^2
   90^2  +    56^2 =    106^2
  105^2  +    56^2 =    119^2
  192^2  +    56^2 =    200^2
  390^2  +    56^2 =    394^2
  783^2  +    56^2 =    785^2

確かに48だと多めだけど、取り立てて面白くない。当たり前のような。

むしろ、

-----(193)------
18624^2  +   193^2 =  18625^2
-----(194)------
 9408^2  +   194^2 =   9410^2

のあたりのほうが面白い。

ともあれ、これをHaskellとかRubyで計算するにはどうするんだろうかなと思って、permutationで組み合わせを作って、48を含むものでfilterして……、とか考えていたけど、はっきり言ってfor文万歳でいいやんかという例題でしかなかった。

と、いいつつRubyでやってみた。1から200までの数字の3つの組み合わせで直角三角形になっているもののリストを作って、このうち48が含まれるものを探す。

a = (1..200).to_a.combination(3).find_all{|i| i[0]**2+i[1]**2 == i[2]**2}

=> [[3, 4, 5], [5, 12, 13], [6, 8, 10], [7, 24, 25], [8, 15, 17], [9,
12, 15], [9, 40, 41], [10, 24, 26], [11, 60, 61], [12, 16, 20], [12,
35, 37], [13, 84, 85], [14, 48, 50], [15, 20, 25], [15, 36, 39], [15,
112, 113], [16, 30, 34], [16, 63, 65], [17, 144, 145], [18, 24, 30],
[18, 80, 82], [19, 180, 181], [20, 21, 29], [20, 48, 52], [20, 99,
101], [21, 28, 35], [21, 72, 75], [22, 120, 122], [24, 32, 40], [24,
45, 51], [24, 70, 74], [24, 143, 145], [25, 60, 65], [26, 168, 170],
[27, 36, 45], [27, 120, 123], [28, 45, 53], [28, 96, 100], [28, 195,
197], [30, 40, 50], [30, 72, 78], [32, 60, 68], [32, 126, 130], [33,
44, 55], [33, 56, 65], [33, 180, 183], [35, 84, 91], [35, 120, 125],
[36, 48, 60], [36, 77, 85], [36, 105, 111], [36, 160, 164], [39, 52,
65], [39, 80, 89], [40, 42, 58], [40, 75, 85], [40, 96, 104], [42, 56,
70], [42, 144, 150], [44, 117, 125], [45, 60, 75], [45, 108, 117],
[48, 55, 73], [48, 64, 80], [48, 90, 102], [48, 140, 148], [48, 189,
195], [49, 168, 175], [50, 120, 130], [51, 68, 85], [51, 140, 149],
[52, 165, 173], [54, 72, 90], [55, 132, 143], [56, 90, 106], [56, 105,
119], [56, 192, 200], [57, 76, 95], [57, 176, 185], [60, 63, 87], [60,
80, 100], [60, 91, 109], [60, 144, 156], [60, 175, 185], [63, 84,
105], [64, 120, 136], [65, 72, 97], [65, 156, 169], [66, 88, 110],
[66, 112, 130], [69, 92, 115], [70, 168, 182], [72, 96, 120], [72,
135, 153], [72, 154, 170], [75, 100, 125], [75, 180, 195], [78, 104,
130], [78, 160, 178], [80, 84, 116], [80, 150, 170], [81, 108, 135],
[84, 112, 140], [84, 135, 159], [85, 132, 157], [87, 116, 145], [88,
105, 137], [88, 165, 187], [90, 120, 150], [93, 124, 155], [95, 168,
193], [96, 110, 146], [96, 128, 160], [99, 132, 165], [99, 168, 195],
[100, 105, 145], [102, 136, 170], [104, 153, 185], [105, 140, 175],
[108, 144, 180], [111, 148, 185], [114, 152, 190], [117, 156, 195],
[119, 120, 169], [120, 126, 174], [120, 160, 200], [130, 144, 194]]

>> a.find_all{|i| i.member?(48)}
=> [[14, 48, 50], [20, 48, 52], [36, 48, 60], [48, 55, 73], [48, 64,
80], [48, 90, 102], [48, 140, 148], [48, 189, 195]]

8つしか見つかってないけど、上限を上げれば10個見つかる。

48が含まれる組み合わせ以外でも2乗の計算をしまくってるのは効率が悪いので、本当は順序が逆なんだろうけど。

うーん、この例だと、member?(48)の結果はリスト中で最初にマッチした値が戻ってくる。もし含まれないとnil。ということは、すべてを1つのメソッドチェーンにしようと思っても、Nil#find_allなんてメソッドはないよと怒られるということか。このへんがMaybeモナドのいいところかしら?