有向グラフの最終サイクルを数える


8

仕事

単純な有向グラフの最終サイクル数を正確にカウントするプログラムまたは関数を、選択した言語で作成する必要があります。

この特定の種類の有向グラフは、n個の整数の配列として表され、それぞれが1からn(または言語が0から数える場合は0からn-1)の間で独立して選択されたランダムな値を持ちます。グラフは、1つのインデックス(ノード)から、開始インデックスで見つかった値と一致するインデックスを指す矢印と考えることができます。

関数は、最大n = 1024またはそれより小さい整数サイズの大きなグラフを受け入れることができる必要があります。

n = 10について次のグラフを考えてみます。

[9, 7, 8, 10, 2, 6, 3, 10, 6, 8]

インデックス1には9が含まれているため、インデックス1からインデックス9への矢印があります。インデックス9には6が含まれているため、矢印9-> 6があります。インデックス6には6が含まれています。

インデックス2には7が含まれます。インデックス7には3が含まれます。インデックス3には8が含まれます。インデックス8には10が含まれます。インデックス10には8が含まれるため、2番目のターミナルサイクルになります(8-> 10-> 8-> 10など)。 )。

インデックス4->10。2番目のターミナルサイクルに入ります。同様に、インデックス5-> 2-> 7-> 3->8。これも2番目のターミナルサイクルの一部です。

この時点で、すべてのインデックス(ノード)がチェックされ、すべてのパスが追跡され、2つの固有のターミナルサイクルが識別されています。したがって、この有向グラフの最終サイクルの数であるため、関数は2返す必要があります。

得点

最小のコードを目指しますが、ターミナルサイクルが正しくカウントされることを確認してください。1週間の勝利後の最短コード。

テストケース

コードの正しさを確認するためのテストケースをいくつか示します。言語が0から始まる配列インデックスをカウントする場合は、範囲外のインデックスを防ぐために、各配列要素の値から1を差し引く必要があります。

n = 32、5サイクル:

[8, 28, 14, 8, 2, 1, 13, 15, 30, 17, 9, 8, 18, 19, 30, 3, 8, 25, 23, 12, 6, 7, 19, 24, 17, 7, 21, 20, 29, 15, 32, 32]

n = 32、4サイクル:

[20, 31, 3, 18, 18, 18, 8, 12, 25, 10, 10, 19, 3, 9, 18, 1, 13, 5, 18, 23, 20, 26, 16, 22, 4, 16, 19, 31, 21, 32, 15, 22]

n = 32、3サイクル:

[28, 13, 17, 14, 4, 31, 11, 4, 22, 6, 32, 1, 13, 15, 7, 19, 10, 28, 9, 22, 5, 26, 17, 8, 6, 13, 7, 10, 9, 30, 23, 25]

n = 32、2サイクル:

[25, 23, 22, 6, 24, 3, 1, 21, 6, 18, 20, 4, 8, 5, 16, 10, 15, 32, 26, 25, 27, 14, 13, 12, 9, 9, 29, 8, 13, 31, 32, 1]

n = 32、1サイクル:

[6, 21, 15, 14, 22, 12, 5, 32, 29, 3, 22, 23, 6, 16, 20, 2, 16, 25, 9, 22, 13, 2, 19, 20, 26, 19, 32, 3, 32, 19, 28, 16]

n = 32、1サイクル:

[8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 1, 2, 3, 4, 5, 6, 7]

n = 1024、6サイクル:

[239, 631, 200, 595, 178, 428, 582, 191, 230, 551, 223, 61, 564, 463, 568, 527, 143, 403, 154, 236, 928, 650, 14, 931, 236, 170, 910, 782, 861, 464, 378, 748, 468, 779, 440, 396, 467, 630, 451, 130, 694, 167, 594, 115, 671, 853, 612, 238, 464, 771, 825, 471, 167, 653, 561, 337, 585, 986, 79, 506, 192, 873, 184, 617, 4, 259, 4, 662, 623, 694, 859, 6, 346, 431, 181, 703, 823, 140, 635, 90, 559, 689, 118, 117, 130, 248, 931, 767, 840, 158, 696, 275, 610, 217, 989, 640, 363, 91, 129, 399, 105, 770, 870, 800, 429, 473, 119, 908, 481, 337, 504, 45, 1011, 684, 306, 126, 215, 729, 771, 5, 302, 992, 380, 824, 868, 205, 807, 917, 407, 759, 181, 640, 685, 795, 258, 180, 900, 20, 773, 546, 866, 564, 761, 632, 895, 968, 980, 651, 225, 676, 18, 685, 784, 208, 227, 3, 267, 852, 57, 487, 566, 633, 849, 309, 543, 145, 575, 811, 621, 560, 492, 24, 665, 66, 851, 168, 262, 259, 754, 481, 565, 768, 172, 1012, 241, 3, 370, 985, 389, 82, 779, 744, 829, 836, 249, 975, 909, 840, 226, 867, 499, 192, 909, 972, 735, 252, 785, 545, 486, 186, 1011, 89, 939, 649, 110, 119, 185, 836, 717, 545, 938, 621, 946, 94, 363, 721, 177, 747, 59, 819, 146, 283, 821, 547, 654, 941, 755, 18, 449, 367, 499, 944, 62, 553, 435, 344, 900, 25, 251, 920, 902, 99, 326, 98, 495, 385, 929, 865, 327, 725, 674, 33, 173, 429, 873, 558, 90, 460, 366, 543, 583, 954, 792, 213, 536, 670, 49, 738, 802, 1015, 23, 915, 119, 263, 307, 601, 474, 971, 826, 613, 446, 37, 145, 894, 901, 307, 906, 886, 990, 89, 798, 384, 487, 822, 354, 768, 902, 163, 179, 134, 920, 439, 619, 215, 94, 709, 744, 366, 543, 349, 347, 2, 438, 141, 486, 19, 998, 500, 857, 955, 932, 1, 587, 195, 646, 550, 887, 626, 400, 348, 154, 808, 678, 873, 186, 282, 168, 993, 722, 56, 345, 5, 226, 328, 22, 894, 658, 264, 13, 803, 791, 359, 217, 997, 168, 578, 952, 734, 964, 898, 659, 628, 980, 15, 31, 439, 13, 875, 687, 1004, 1023, 165, 642, 561, 897, 711, 124, 404, 346, 723, 774, 352, 784, 276, 395, 14, 443, 343, 153, 510, 590, 172, 215, 130, 106, 295, 906, 133, 758, 483, 898, 391, 760, 702, 972, 721, 611, 592, 1001, 724, 934, 59, 831, 171, 253, 869, 431, 538, 20, 648, 76, 351, 103, 33, 385, 852, 437, 470, 95, 434, 408, 430, 994, 366, 706, 809, 532, 161, 388, 668, 245, 965, 365, 913, 471, 927, 245, 256, 805, 540, 380, 995, 446, 657, 545, 573, 955, 499, 322, 949, 635, 401, 185, 421, 626, 534, 429, 930, 633, 563, 348, 626, 518, 682, 233, 775, 444, 42, 199, 57, 271, 683, 397, 883, 620, 768, 8, 331, 497, 19, 340, 900, 919, 497, 276, 78, 252, 164, 764, 927, 242, 270, 759, 824, 945, 886, 262, 59, 439, 217, 720, 519, 862, 626, 326, 339, 589, 16, 565, 947, 604, 144, 87, 520, 256, 240, 336, 685, 361, 998, 805, 678, 24, 980, 203, 818, 855, 85, 276, 822, 183, 266, 347, 8, 663, 620, 147, 189, 497, 128, 357, 855, 507, 275, 420, 755, 131, 469, 672, 926, 859, 156, 127, 986, 489, 803, 433, 622, 951, 83, 862, 108, 192, 167, 862, 242, 519, 574, 358, 549, 119, 630, 60, 925, 414, 479, 330, 927, 94, 767, 562, 919, 1011, 999, 908, 113, 932, 632, 403, 309, 838, 341, 179, 708, 847, 472, 907, 537, 516, 992, 944, 615, 778, 801, 413, 653, 690, 393, 452, 394, 596, 545, 591, 136, 109, 942, 546, 57, 626, 61, 587, 862, 829, 988, 965, 781, 849, 843, 815, 60, 928, 784, 388, 341, 491, 565, 83, 110, 164, 38, 1024, 859, 297, 520, 327, 733, 699, 631, 78, 178, 671, 895, 818, 637, 99, 425, 933, 248, 299, 333, 144, 323, 105, 849, 942, 767, 265, 72, 204, 547, 934, 916, 304, 919, 273, 396, 665, 452, 423, 471, 641, 675, 60, 388, 97, 963, 902, 321, 826, 476, 782, 723, 99, 735, 893, 565, 175, 141, 70, 918, 659, 935, 492, 751, 261, 362, 849, 593, 924, 590, 982, 876, 73, 993, 767, 441, 70, 875, 640, 567, 920, 321, 46, 938, 377, 905, 303, 736, 182, 626, 899, 512, 894, 744, 254, 984, 325, 694, 6, 367, 532, 432, 133, 938, 74, 967, 725, 87, 502, 946, 708, 122, 887, 256, 595, 169, 101, 828, 696, 897, 961, 376, 910, 82, 144, 967, 885, 89, 114, 215, 187, 38, 873, 125, 522, 884, 947, 962, 45, 585, 644, 476, 710, 839, 486, 634, 431, 475, 979, 877, 18, 226, 656, 573, 3, 29, 743, 508, 544, 252, 254, 388, 873, 70, 640, 918, 93, 508, 853, 609, 333, 378, 172, 875, 617, 167, 771, 375, 503, 221, 624, 67, 655, 465, 272, 278, 161, 840, 52, 1016, 909, 567, 544, 234, 339, 463, 621, 951, 962, 1019, 383, 523, 279, 780, 838, 984, 999, 29, 897, 564, 762, 753, 393, 205, 31, 150, 490, 156, 796, 586, 676, 773, 465, 489, 1024, 433, 214, 701, 480, 604, 280, 241, 563, 943, 911, 12, 400, 261, 883, 999, 207, 618, 141, 959, 767, 978, 461, 992, 982, 272, 143, 404, 645, 331, 348, 783, 698, 827, 82, 145, 536, 449, 852, 750, 789, 413, 913, 420, 14, 499, 285, 533, 223, 75, 591, 994, 884, 237, 63, 411, 563, 611, 801, 173, 759, 278, 318, 772, 1018, 48, 440, 333, 611, 834, 423, 583, 22, 716, 393, 794, 83, 83, 864, 859, 600, 525, 808, 569, 95, 952, 852, 567, 651, 2, 984, 906, 992, 747, 602, 143, 547, 1008, 940, 245, 633, 378, 193, 771, 965, 648, 437, 873, 591, 664, 271, 777, 274, 742, 68, 429, 825, 144, 55, 272, 279, 6, 400, 485, 66, 311, 663, 441, 23, 988, 726, 48, 624, 302, 617, 120, 653, 810, 641, 142]

確かに、これは大きなファットn = 1024テストケースです。うまくいけば、Stack Exchange用にパッケージ化しているときに、それを屠殺しなかった。
ホスゲン2014年

明確化:言語がゼロベースの配列を使用する場合、配列内の数値はすべてゼロベースか、そうでないか?
Ypnypn 2014年

それは正解です。配列内の数値は、常に有効な配列インデックスを指します。私は1から始まる「ヒューマンスタイル」のカウント方法を使用しました。これは私の言語が使用しているためであり、自分の例を台無しにしたくありませんでした。
ホスゲン

回答:


2

GolfScript、25文字

:I{{I=.}I.+,*]I,>$0=}%.&,

Keith Randallのソリューションと同じアプローチですが、GolfScriptを使用します。GolfScriptにはゼロインデックスの配列があることに注意してください。オンラインテスター

:I        # Assign input to variable I
{         # Foreach item in I
  {I=.}   # Code block: take the n-th list item
  I.+,*   # Iterate the code block 2*len(I) times
  ]       # and gather result in an array
  I,>     # Take last len(I) items
  $0=     # Get minimum
}%
.&        # Take unique items
,         # Count

信じられないほど多くのことは、非常に少ないコードで達成されます。
DavidC、2014

それは印象的に短いです。
ホスゲン2014年

5

Mathematica 69

コード

これにより、グラフコンポーネントの数がわかります。

f@l_ := Length@WeaklyConnectedComponents@Graph@Thread[Range@Length@l -> l]

最初のテストケース:

v = {8, 28, 14, 8, 2, 1, 13, 15, 30, 17, 9, 8, 18, 19, 30, 3, 8, 25, 23, 12, 6, 7, 19, 24, 17, 7, 21, 20, 29, 15, 32, 32}
f[v]

5


分析

インデックス間の有向辺のリストを作成します(例1を使用)。

Thread[Range@Length@v -> v

{1-> 8、2-> 28、3-> 14、4-> 8、5-> 2、6-> 1、7-> 13、8-> 15、9-> 30、10-> 17 、11-> 9、12-> 8、13-> 18、14-> 19、15-> 30、16-> 3、17-> 8、18-> 25、19-> 23、20-> 12 、21-> 6、22-> 7、23-> 19、24-> 24、25-> 17、26-> 7、27-> 21、28-> 20、29-> 29、30-> 15 、31-> 32、32-> 32}


Graph グラフのコンポーネントを示すグラフを描画します。

ImagePaddingそして、VertexLabels インデックスを表示するために、ここで使用されています。

Graph[Thread[Range[Length@v] -> v], ImagePadding -> 30, VertexLabels -> "Name"]

コンポーネント

WeaklyConnectedComponents 各コンポーネントの頂点のリストを返します。

Length コンポーネントの数を返します。

c = WeaklyConnectedComponents[g]
Length[c]

{{17、10、25、8、18、1、4、12、15、13、6、20、30、7、21、28、9、22、26、27、2、11、5}、{ 14、3、19、16、23}、{32、31}、{24}、{29}}

5


1024要素のサンプルリストのタイミング:

タイミング:0.002015秒

f[z] // AbsoluteTiming

{0.002015、6}


面白くするために、これがグラフ化された最終テストケースの写真です。頂点ラベルを省略しました。数が多すぎます。

Graph[Thread[Range[Length@z] -> z], GraphLayout -> "RadialEmbedding"]

グラフz


1
25文字の関数名にもかかわらず、非常に簡潔です。これは勝つことが難しいかもしれません。また、Choose Your Own Adventureストーリーのようにも見えます。
ホスゲン

WeaklyConnectedComponents非常に冗長です。しかし、それは多くの仕事をします。(Jや類似の言語を簡潔にするために過小評価してはいけません。)
DavidC

ある時点で、Wolframはアイデアを使い果たし、Stack Exchangeの回答をライブラリ関数に変換し始めるのではないかと思います。新しいリリースを正当化するためです。
ホスゲン2014

はい、私はあなたの意味を理解しています。現在Mathematicaには文字通り何千もの関数が組み込まれています。奇妙なのは、それらがうまく連携していることです。
DavidC、2014

式書き換えの最初のフレームワークは、のろわれた良いアイデアでした。今度は、元に戻すの複数のレベルを実装する場合のみ
ホスゲン

2

パイソン、132の 116文字

def T(V):
 n=len(V);r=range(n);s={}
 for i in r:
    p=[i]
    for k in r+r:p+=[V[p[-1]]]
    s[min(p[n:])]=1
 return len(s)

各インデックスについて、nホップのエッジをたどります。これにより、ターミナルサイクルにいることが保証されます。次に、さらにnホップを追跡し、そのサイクルの最小インデックスを見つけます。ターミナルサイクルの総数は、検出された異なる最小値の数になります。


私はこの方法が好きです。おそらく、「i in r」ループを組み合わせる方法はありますか?
ホスゲン

#define F for i in rPythonではできないこと、C(++)スタイルで
悲しい

1

Pythonの場合:

def c(l):
    if(l==[]):
        return 0
    elif (l[-1]==len(l)):
        return c(l[:-1])+1
    else:
        return c([[x,l[-1]][x==len(l)] for x in l[:-1]])

1
これはコードゴルフです。提出物にバイト数を追加してください。不要な空白を削除したい場合があります。
マーティンエンダー2014年

1
不要な括弧も同様です。if条件では必要ありません。
user12205 2014年

c=lambda l:0 if l==[] else c(l[:-1])+1 if l[-1]==len(l) else c([[x,l[-1]][x==len(l)] for x in l[:-1]])102バイトです。
ɐɔıʇǝɥʇuʎs

1

J- 61 53文字

これはおかしな話でした。

#@([:#/.~[:+./ .*"1/~@e.<~.@(],;@:{~)^:_&.>])@:(<@<:)

<@<:ボックスの一覧、およびインデックスのボックスのように見えるが、Jグラフ、にリストを回しi、そのノードのすべてのノードが含まiが接続すると。Jはゼロからインデックス付けするため、で<:ボックス化する前にすべてを1ずつデクリメントするために使用します<

   (<@<:) 9 7 8 10 2 6 3 10 6 8
+-+-+-+-+-+-+-+-+-+-+
|8|6|7|9|1|5|2|9|5|7|
+-+-+-+-+-+-+-+-+-+-+

<~.@(],;@:{~)^:_&.>]それから到達可能なすべてのノードのリストに各ノードをオンにします。<...&.>]これは、各ノードに起こる作るための責任があり、そして~.@(],;@:{~)^:_実際にから来ているこの「ノード到達可能な」タスクのJゴルフ私は数週間前でした。

   (<~.@(],;@:{~)^:_&.>])@:(<@<:) 9 7 8 10 2 6 3 10 6 8
+---+-------+---+---+---------+-+-----+---+-+---+
|8 5|6 2 7 9|7 9|9 7|1 6 2 7 9|5|2 7 9|9 7|5|7 9|
+---+-------+---+---+---------+-+-----+---+-+---+

e.興味深いタスクを実行します。グラフの「到達可能性」クロージャー(有向エッジX→YおよびY→Zがある場合、エッジX→Zを追加するバージョンのグラフ)にN個のノードとE個のエッジがある場合e.、このグラフではN行E列のブール行列。対応するノードが到達可能なノードをこのエッジと共有する場合はTrue。混乱しますが、我慢してください。

   ([:e.<~.@(],;@:{~)^:_&.>])@:(<@<:) 9 7 8 10 2 6 3 10 6 8
1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0
0 0 1 1 1 1 1 1 1 1 0 1 1 1 1 0 1 1 1 1 1 0 1 1
0 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 1
0 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 1
0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0
0 0 0 1 1 1 1 1 1 1 0 0 1 1 1 0 1 1 1 1 1 0 1 1
0 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 1
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0
0 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 1

次に、ターミナルサイクルの数、つまり列間でTrueを共有するグループの数を見つける必要があります。行の乗算表のようなものを作りたい("1/~)と、一種の内積を乗算として使用します。つまり、ペアでANDをとり、すべての結果をORで取ります(+./ .*)。結果の行列は、2つの行がそれらの間で少なくとも1つの列を共有する各位置にTrueを持つ正方形のテーブルです。

   ([:+./ .*"1/~@e.<~.@(],;@:{~)^:_&.>])@:(<@<:) 9 7 8 10 2 6 3 10 6 8
1 0 0 0 0 1 0 0 1 0
0 1 1 1 1 0 1 1 0 1
0 1 1 1 1 0 1 1 0 1
0 1 1 1 1 0 1 1 0 1
0 1 1 1 1 0 1 1 0 1
1 0 0 0 0 1 0 0 1 0
0 1 1 1 1 0 1 1 0 1
0 1 1 1 1 0 1 1 0 1
1 0 0 0 0 1 0 0 1 0
0 1 1 1 1 0 1 1 0 1

あとは、行パターンの種類がいくつあるかを確認するだけです。つまり、同じことを行います。同じ種類の行を/.~グループ化し(#)、各グループの数を報告し()、次にグループの数を取得します(#@)。

   #@([:#/.~[:+./ .*"1/~@e.<~.@(],;@:{~)^:_&.>])@:(<@<:) 9 7 8 10 2 6 3 10 6 8
2

他の例での使用:

   tcc =: #@([:#/.~[:+./ .*"1/~@e.<~.@(],;@:{~)^:_&.>])@:(<@<:)  NB. name
   tcc 8 28 14 8 2 1 13 15 30 17 9 8 18 19 30 3 8 25 23 12 6 7 19 24 17 7 21 20 29 15 32 32
5
   tcc 20 31 3 18 18 18 8 12 25 10 10 19 3 9 18 1 13 5 18 23 20 26 16 22 4 16 19 31 21 32 15 22
4
   tcc 6 21 15 14 22 12 5 32 29 3 22 23 6 16 20 2 16 25 9 22 13 2 19 20 26 19 32 3 32 19 28 16
1
   tcc tentwentyfour  NB. the 1024-node example
6

残念ながら、1024要素のケースは、終了するのに非常に長い時間がかかります。以前のバージョン<:@#@((#~0={.+/@:*"1])^:a:)@e.@(~.@(],;@:{~)^:_&.>~<)@:(<@<:)(61文字)では、この処理に1秒ほどかかりました。


ああそう、古き良きe。機能、知っておくべきだった。o_Oあなた​​のコードを見て、私はうんざりしています。よくやった。
ホスゲン2014

0

パイソン(96)

非常に、非常に、非常に、非常に非常にuser2228078の回答に基づいています。まったく同じように機能しますが、よりゴルフに適しています。

c=lambda l:(1+c(l[:-1])if l[-1]==len(l)else c([[x,l[-1]][x==len(l)]for x in l[:-1]]))if l else 0

(技術的な質問:他の人が答えるゴルフはコミュニティwikiである必要がありますか?)


盗用する私の祝福があります。それをコミュニティの努力としましょう。
ホスゲン

@ user2790167かしこまりました;)
ɐɔıʇǝɥʇuʎsMay

0

パイソン(89)(87)

def c(G):
 u=i=0
 for _ in G:
  j=i;i+=1
  while j>=0:G[j],j=~i,G[j]
  u+=j==~i
 return u

主なアイデアは、各ノードから順番に開始し、そこからのパスに沿って歩き、訪問した各ノードに、開始したノードに固有のラベルを付けることです。マークされたノードに到達した場合は、歩行を停止し、そのマークが開始ノードに対応するものかどうかを確認します。もしそうなら、ループをたどったはずなので、サイクルのカウントを1つ増やします。

コードで、uはサイクルカウンター、iは開始ノード、jは現在歩いているノードです。開始ノードに対応するラベルは、常に負のiビット補数~iです。ノードをその値にポイントしてマークし、実際にポイントしたノードを上書きします(忘れられる前にそのノードに移動するように注意してください)。

直後に負の値の「ノード」に移動すると、マークされたノードにヒットしたことがわかります。その負の値が現在のラベルであるかどうかをチェックして、サイクルを歩いたかどうかを確認します。歩く各ノードは効果的に削除されるため、各サイクルは1回だけ歩くことになります。

文字を保存iして、ダミーのループ変数を使って手動でカウントアップ_for i in range(len(G))ます。Pythonリストは0インデックスです。彼らは代わりに1-インデックスされた場合、我々は書き込むことによって2つの文字を救うことができるj=i=i+1持っているiと、j最初のループの1になり、書き込みj>0の代わりにj>=0

編集:

iインデックスではなくリストの要素を反復処理することで2つの文字を保存できます。これは、どのエッジからもポイントされていないノードは関係ないためです。set(G)ただし、他の複数のノードが指している開始ノードが繰り返されないようにするため、繰り返し処理する必要があります。

def c(G):
 u=0
 for i in set(G):
  j=i
  while j>=0:G[j],j=~i,G[j]
  u+=j==~i
 return u

まだ投稿されている最短のPythonコード、および興味深い「チョークマーキング」メソッド。私はそれが好きです。
ホスゲン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.