[{"data":1,"prerenderedAt":1099},["ShallowReactive",2],{"$f0n6A_-T9UdIrfLvAodNlGjw2p9TXYnhlr0ZI7dNYVaU":3,"cfn":153},[4,56,97,127],{"slug":5,"title":6,"lang":7,"desc":8,"date":9,"tags":10,"edit":9,"readingTime":11,"toc":16,"content":55},"sorting-sins","A Whimsical \"Sin of Sorting\": What If You Use a Random Comparator for Sorting?","en","What happens if I use a random function as a comparator when sorting?","2026-07-15T00:00:00.000Z",[],{"text":12,"minutes":13,"time":14,"words":15},"7 min read",6.67,400200,1334,[17,21,24,27,31,34,37,40,43,46,49,52],{"level":18,"text":19,"slug":20},2,"A Whimsical Thought","a-whimsical-thought",{"level":18,"text":22,"slug":23},"Initial Guesses","initial-guesses",{"level":18,"text":25,"slug":26},"Behavior by Language","behavior-by-language",{"level":28,"text":29,"slug":30},3,"JavaScript (Node.js v24.14.0 \u002F Bun.js 1.3.14 \u002F Deno.js 2.9.2)","javascript-node-js-v24-14-0-bun-js-1-3-14-deno-js-2-9-2",{"level":28,"text":32,"slug":33},"Python (Python 3.14.3)","python-python-3-14-3",{"level":28,"text":35,"slug":36},"Go (go 1.26.2)","go-go-1-26-2",{"level":28,"text":38,"slug":39},"C++ (clang 22.1.8)","c-clang-22-1-8",{"level":28,"text":41,"slug":42},"C# (.NET 10.0.301)","c-net-10-0-301",{"level":28,"text":44,"slug":45},"Java (JDK 26.0.1)","java-jdk-26-0-1",{"level":28,"text":47,"slug":48},"Rust (rustc 1.97.0)","rust-rustc-1-97-0",{"level":18,"text":50,"slug":51},"Rust: But Is That Really the Whole Story?","rust-but-is-that-really-the-whole-story",{"level":18,"text":53,"slug":54},"Summary","summary","\u003Ch2 id=\"a-whimsical-thought\">A Whimsical Thought\u003C\u002Fh2>\n\u003Cp>Tonight, perhaps out of sheer boredom, a thought struck me: \u003Cstrong>What if I use a random function as a comparator when sorting?\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>Normally, the comparator passed to \u003Ccode>sort\u003C\u002Fcode> should return a boolean indicating \u003Ccode>a &lt; b\u003C\u002Fcode> to decide which element comes first. But what if I make it return randomly? How would the sorting algorithm behave?\u003C\u002Fp>\n\u003Cp>And how would different languages handle this kind of &quot;violation&quot; or undefined behavior? Would they produce a shuffled result, or would they throw an error?\u003C\u002Fp>\n\u003Cp>Thus, this repository was born: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FKuriyona\u002Fsorting-sins\" target=\"_blank\" rel=\"noopener noreferrer\">sorting-sins\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Fp>\n\u003Cp>The languages tested in this project are:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>JavaScript (Node.js &amp; Bun.js &amp; Deno)\u003C\u002Fli>\n\u003Cli>Python\u003C\u002Fli>\n\u003Cli>Go\u003C\u002Fli>\n\u003Cli>C++\u003C\u002Fli>\n\u003Cli>C#\u003C\u002Fli>\n\u003Cli>Java\u003C\u002Fli>\n\u003Cli>Rust (latest &amp; \u003Cstrong>v1.80.0\u003C\u002Fstrong>)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cblockquote>\n\u003Cp>I wrote the code for JavaScript, Python, and Go myself; the other languages were generated with AI assistance.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"initial-guesses\">Initial Guesses\u003C\u002Fh2>\n\u003Cp>Initially, I thought other languages might produce something resembling random output, or perhaps throw an error.\u003C\u002Fp>\n\u003Cp>I did expect JavaScript to produce a random result, and maybe Rust to fail at compile time — that's the stereotype I have of these two languages.\u003C\u002Fp>\n\u003Ch2 id=\"behavior-by-language\">Behavior by Language\u003C\u002Fh2>\n\u003Ch3 id=\"javascript-node-js-v24-14-0-bun-js-1-3-14-deno-js-2-9-2\">JavaScript (Node.js v24.14.0 \u002F Bun.js 1.3.14 \u002F Deno.js 2.9.2)\u003C\u002Fh3>\n\u003Cp>Using \u003Ccode>Math.random() - 0.5\u003C\u002Fcode>, i.e., a random float in [-0.5, 0.5], as the comparator, run three times:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-js\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(() \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">=>\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\"> Math\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">random\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">() \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0.5\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Run 1: [67, 68, 87, 44, 50, 16, 42, 3, 24, 31, 1, 74, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 2: [23, 69, 5, 47, 38, 36, 8, 27, 32, 11, 10, 0, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 3: [50, 15, 7, 19, 34, 31, 32, 11, 47, 48, 51, 52, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Looks random, as expected.\u003C\u002Fp>\n\u003Ch3 id=\"python-python-3-14-3\">Python (Python 3.14.3)\u003C\u002Fh3>\n\u003Cp>Using \u003Ccode>functools.cmp_to_key\u003C\u002Fcode> to convert the comparator into a key function.\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-python\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">arr.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">key\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">cmp_to_key\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">lambda\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66;font-style:italic\"> a\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66;font-style:italic\">b\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">: random.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">choice\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">([\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">])))\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Run 1: [79, 0, 44, 53, 38, 66, 90, 19, 3, 87, 31, 5, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 2: [0, 80, 48, 22, 30, 79, 45, 95, 70, 84, 76, 74, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 3: [17, 1, 73, 89, 80, 52, 26, 35, 30, 53, 9, 34, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Python also produces random-looking results — not surprising.\u003C\u002Fp>\n\u003Ch3 id=\"go-go-1-26-2\">Go (go 1.26.2)\u003C\u002Fh3>\n\u003Cp>I'm not very familiar with Go, but I managed to write the code.\u003C\u002Fp>\n\u003Cp>Using \u003Ccode>sort.Slice\u003C\u002Fcode> with \u003Ccode>rand.Intn(2) == 0\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-go\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E06C75\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Slice\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">func\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">i\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">j\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\"> int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">bool\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\"> rand\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Intn\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">2\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">})\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Run 1: [46, 1, 55, 20, 11, 66, 56, 88, 80, 54, 41, 77, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 2: [68, 21, 62, 78, 20, 7, 3, 16, 72, 69, 85, 36, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 3: [26, 59, 90, 82, 20, 54, 79, 16, 43, 67, 77, 41, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Again, looks random.\u003C\u002Fp>\n\u003Ch3 id=\"c-clang-22-1-8\">C++ (clang 22.1.8)\u003C\u002Fh3>\n\u003Cp>Next, the C++ version:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-cpp\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">std\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">begin\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(), \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">end\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(), [\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">&#x26;\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">gen\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">](\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> std::\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">uniform_int_distribution\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">&#x3C;>(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">)(gen) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">});\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Run 1: [73, 3, 10, 46, 88, 72, 26, 64, 38, 75, 91, 50, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 2: [20, 76, 33, 3, 86, 47, 34, 69, 94, 24, 71, 87, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 3: [95, 1, 22, 99, 21, 94, 6, 57, 51, 85, 10, 11, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>C++ simply runs the code, no matter what input it gets.\u003C\u002Fp>\n\u003Ch3 id=\"c-net-10-0-301\">C# (.NET 10.0.301)\u003C\u002Fh3>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-cs\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">Array\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, (\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">a\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">b\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) => \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Next\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">2\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> ? \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> : \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Run 1: [80, 57, 97, 92, 32, 3, 74, 10, 53, 35, 15, 29, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 2: [3, 27, 58, 98, 56, 28, 39, 82, 94, 53, 9, 50, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 3: [77, 19, 2, 61, 4, 15, 92, 95, 21, 7, 62, 84, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>However, when the array size increases to N = 100,000, the situation changes.\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Unhandled exception. System.ArgumentException: Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: 'System.Comparison`1[System.Int32]'.\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>C# can detect the inconsistent comparer once the array reaches a certain size.\u003C\u002Fp>\n\u003Ch3 id=\"java-jdk-26-0-1\">Java (JDK 26.0.1)\u003C\u002Fh3>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-java\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">((a, b) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">->\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\"> rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">nextInt\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">3\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Run 1: [0, 3, 8, 25, 5, 27, 20, 23, 9, 32, 18, 14, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 2: [25, 79, 1, 8, 14, 17, 2, 5, 16, 9, 3, 24, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 3: [50, 12, 0, 5, 11, 1, 22, 2, 3, 19, 16, 4, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Like C#, when the array size increases to N = 100,000, the situation changes.\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Exception in thread \"main\" java.lang.IllegalArgumentException: Comparison method violates its general contract!\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Java can also detect the comparer issue once the array reaches a certain size.\u003C\u002Fp>\n\u003Cp>This check was introduced in Java 7. Before Java 7, Java would silently produce a jumbled result without throwing an error.\u003C\u002Fp>\n\u003Ch3 id=\"rust-rustc-1-97-0\">Rust (rustc 1.97.0)\u003C\u002Fh3>\n\u003Cp>Finally, Rust. The AI used \u003Ccode>rand::Rng\u003C\u002Fcode> to decide between \u003Ccode>Ordering::Less\u003C\u002Fcode> and \u003Ccode>Ordering::Greater\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-rust\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort_by\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">|\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">_\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">_\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">|\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">    if\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\"> rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">gen\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">bool\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">>() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">        Ordering\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">Less\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">    } \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">else\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">        Ordering\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">Greater\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">});\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Run 1: thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 2: thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>Run 3: thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Error message:\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>user-provided comparison function does not correctly implement a total order\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>This was expected. \u003Cstrong>Rust is the only language that panics\u003C\u002Fstrong> (though not at compile time, as I had initially guessed).\u003C\u002Fp>\n\u003Cp>But it does detect at runtime whether your comparator implements a total order, and panics immediately if it doesn't. This kind of language-level safety boundary checks is exactly the stereotype I have of Rust. Better to crash than to let undefined behavior participate in program execution.\u003C\u002Fp>\n\u003Ch2 id=\"rust-but-is-that-really-the-whole-story\">Rust: But Is That Really the Whole Story?\u003C\u002Fh2>\n\u003Cp>When I finished testing Rust and shared the results with a friend \u003Ca href=\"https:\u002F\u002Flaunchpadx.top\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">LaunchPad\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>, they tried to reproduce it in Grok and found that it did \u003Cstrong>not\u003C\u002Fstrong> panic.\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-1.jpg\" alt=\"sorting-sins-grok-1\">\u003C\u002Fp>\n\u003Cp>This puzzled me, because on my local machine it definitely panicked.\u003C\u002Fp>\n\u003Cp>We naturally suspected a version difference. It turned out the Grok VM was using Rust \u003Ccode>1.75.0 (82e1608df 2023-12-21)\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-2.jpg\" alt=\"sorting-sins-grok-2\">\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-3.jpg\" alt=\"sorting-sins-grok-3\">\u003C\u002Fp>\n\u003Cp>My local version was \u003Ccode>1.97.0 (c980f4866 2026-06-30)\u003C\u002Fcode>, so we tentatively concluded it was a version issue. I initially didn't plan to dig deeper right away, and published the first version of this blog post.\u003C\u002Fp>\n\u003Cp>Shortly after publishing, one of my sisters \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Flfcypo\" target=\"_blank\" rel=\"noopener noreferrer\">lfcypo\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa> provided historical version details and a specific explanation of the changes.\u003C\u002Fp>\n\u003Cp>With her help and subsequent research, the conclusion is that \u003Cstrong>two key PRs\u003C\u002Fstrong> shaped the current behavior:\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>PR\u003C\u002Fth>\n\u003Cth>Summary\u003C\u002Fth>\n\u003Cth>Merged\u003C\u002Fth>\n\u003Cth>Released in\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Frust-lang\u002Frust\u002Fpull\u002F124032\" target=\"_blank\" rel=\"noopener noreferrer\">#124032\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>Replaces sorting implementation — introduces \u003Cstrong>driftsort\u003C\u002Fstrong> (for \u003Ccode>slice::sort\u003C\u002Fcode>) and \u003Cstrong>ipnsort\u003C\u002Fstrong> (for \u003Ccode>slice::sort_unstable\u003C\u002Fcode>). The new implementation actively detects \u003Cstrong>strict weak ordering\u003C\u002Fstrong> violations and panics; older Timsort\u002Fpdqsort could not reliably detect them.\u003C\u002Ftd>\n\u003Ctd>2024-06-21\u003C\u002Ftd>\n\u003Ctd>Rust 1.81.0\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Frust-lang\u002Frust\u002Fpull\u002F128273\" target=\"_blank\" rel=\"noopener noreferrer\">#128273\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>Improves Ord violation help message — changes panic message to \u003Ccode>user-provided comparison function does not correctly implement a total order\u003C\u002Fcode> and improves documentation.\u003C\u002Ftd>\n\u003Ctd>2024-08-11\u003C\u002Ftd>\n\u003Ctd>Rust 1.81.0\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>The new implementation adds runtime consistency checks, actively panicking for many comparators that violate total order, whereas older sort implementations generally did not perform such checks.\u003C\u002Fp>\n\u003Cp>Before 1.81.0 (e.g., 1.80.0): in limited tests, passing a non-deterministic comparator would silently produce seemingly random permutations. Starting from 1.81.0: the sort implementation can detect comparator inconsistency with high probability and panic.\u003C\u002Fp>\n\u003Ch2 id=\"summary\">Summary\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Cstrong>Language\u003C\u002Fstrong>\u003C\u002Fth>\n\u003Cth>\u003Cstrong>Behavior\u003C\u002Fstrong>\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>C++(Clang)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>(seemingly) randomly shuffles\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Python\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>(seemingly) randomly shuffles\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>JavaScript (Node.js \u002F Bun \u002F Deno)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>(seemingly) randomly shuffles\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Go\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>(seemingly) randomly shuffles\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Java\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>N=100 randomly shuffles, N=1,000,000 \u003Cstrong>throws\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>C#\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>N=100 randomly shuffles, N=1,000,000 \u003Cstrong>throws\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Rust(latest)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>panic\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Rust(v1.80.0)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>(seemingly) randomly shuffles\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>Five languages chose to &quot;pretend nothing happened&quot; in the face of a random comparator, while only Rust (since v1.81.0) chose to panic, and Java and C# may throw errors once the array reaches a certain size.\u003C\u002Fp>\n\u003Cp>I will analyze the actual number of executions for various languages ​​and whether there are any patterns in the sorting results later. Anyway, let's leave it at that for now and work on it again when I have time.\u003C\u002Fp>\n",{"slug":5,"title":57,"lang":58,"desc":59,"date":9,"tags":60,"edit":9,"readingTime":61,"toc":66,"content":96},"突発的な思いつき「ソートの罪」：ソートにランダムコンパレータを使ったらどうなるか","ja","ソートの際に比較関数としてランダム関数を使ったら、何が起こるのか？",[],{"text":62,"minutes":63,"time":64,"words":65},"11 min read",10.45,627000,2090,[67,70,73,76,78,80,82,84,86,88,90,93],{"level":18,"text":68,"slug":69},"突発的な思いつき","突発的-思",{"level":18,"text":71,"slug":72},"結果の予想","結果-予想",{"level":18,"text":74,"slug":75},"各言語の振る舞い","各言語-振-舞",{"level":28,"text":77,"slug":30},"JavaScript（Node.js v24.14.0 \u002F Bun.js 1.3.14 \u002F Deno.js 2.9.2）",{"level":28,"text":79,"slug":33},"Python（Python 3.14.3）",{"level":28,"text":81,"slug":36},"Go（go 1.26.2）",{"level":28,"text":83,"slug":39},"C++（clang 22.1.8）",{"level":28,"text":85,"slug":42},"C#（.NET 10.0.301）",{"level":28,"text":87,"slug":45},"Java（JDK 26.0.1）",{"level":28,"text":89,"slug":48},"Rust（rustc 1.97.0）",{"level":18,"text":91,"slug":92},"Rust：だが、本当にそうなのか？","rust-本当",{"level":18,"text":94,"slug":95},"まとめ","","\u003Ch2 id=\"突発的-思\">突発的な思いつき\u003C\u002Fh2>\n\u003Cp>今晩は暇すぎたのか、ふとこんなことを思いついた。\u003Cstrong>もしソートのときに比較関数としてランダム関数を使ったら、何が起こるだろう？\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>通常、\u003Ccode>sort\u003C\u002Fcode> に渡す比較関数は \u003Ccode>a &lt; b\u003C\u002Fcode> の真偽値を返し、どちらが前に来るかを決める。しかし、仮にランダムに返すようにしたら、ソートアルゴリズムはどのような振る舞いをするのだろうか？\u003C\u002Fp>\n\u003Cp>また、各言語はこのような「違反」あるいは未定義動作をどのように扱うのか？ランダムな順序になるのか、それともエラーになるのか？\u003C\u002Fp>\n\u003Cp>そこでこのリポジトリを作った：\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FKuriyona\u002Fsorting-sins\" target=\"_blank\" rel=\"noopener noreferrer\">sorting-sins\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Fp>\n\u003Cp>このプロジェクトでテストした言語は以下の通り：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>JavaScript (Node.js &amp; Bun.js &amp; Deno)\u003C\u002Fli>\n\u003Cli>Python\u003C\u002Fli>\n\u003Cli>Go\u003C\u002Fli>\n\u003Cli>C++\u003C\u002Fli>\n\u003Cli>C#\u003C\u002Fli>\n\u003Cli>Java\u003C\u002Fli>\n\u003Cli>Rust (latest &amp; \u003Cstrong>v1.80.0\u003C\u002Fstrong>)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cblockquote>\n\u003Cp>JavaScript、Python、Go のコードは自分で書いたが、他の言語は AI の助けを借りて書いた。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"結果-予想\">結果の予想\u003C\u002Fh2>\n\u003Cp>最初は、他の言語もランダムに近い結果を出すか、あるいはエラーを返すと思っていた。\u003C\u002Fp>\n\u003Cp>ただ、JavaScript はランダムな結果を出すだろうし、Rust はコンパイル時にエラーになるかもしれないと考えていた。この二つの言語に対するステレオタイプ的なイメージからだ。\u003C\u002Fp>\n\u003Ch2 id=\"各言語-振-舞\">各言語の振る舞い\u003C\u002Fh2>\n\u003Ch3 id=\"javascript-node-js-v24-14-0-bun-js-1-3-14-deno-js-2-9-2\">JavaScript（Node.js v24.14.0 \u002F Bun.js 1.3.14 \u002F Deno.js 2.9.2）\u003C\u002Fh3>\n\u003Cp>\u003Ccode>Math.random() - 0.5\u003C\u002Fcode>、つまり [-0.5, 0.5] のランダムな浮動小数点を比較関数として使い、3回実行：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-js\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(() \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">=>\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\"> Math\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">random\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">() \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0.5\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>1回目：[67, 68, 87, 44, 50, 16, 42, 3, 24, 31, 1, 74, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>2回目：[23, 69, 5, 47, 38, 36, 8, 27, 32, 11, 10, 0, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>3回目：[50, 15, 7, 19, 34, 31, 32, 11, 47, 48, 51, 52, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>ランダムな結果になっている。予想通りだ。\u003C\u002Fp>\n\u003Ch3 id=\"python-python-3-14-3\">Python（Python 3.14.3）\u003C\u002Fh3>\n\u003Cp>\u003Ccode>functools.cmp_to_key\u003C\u002Fcode> を使って比較関数をキー関数に変換する。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-python\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">arr.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">key\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">cmp_to_key\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">lambda\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66;font-style:italic\"> a\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66;font-style:italic\">b\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">: random.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">choice\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">([\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">])))\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>1回目：[79, 0, 44, 53, 38, 66, 90, 19, 3, 87, 31, 5, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>2回目：[0, 80, 48, 22, 30, 79, 45, 95, 70, 84, 76, 74, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>3回目：[17, 1, 73, 89, 80, 52, 26, 35, 30, 53, 9, 34, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>こちらもランダムな結果。特に驚きはない。\u003C\u002Fp>\n\u003Ch3 id=\"go-go-1-26-2\">Go（go 1.26.2）\u003C\u002Fh3>\n\u003Cp>Go はあまり詳しくないが、なんとかコードを書いた。\u003C\u002Fp>\n\u003Cp>\u003Ccode>sort.Slice\u003C\u002Fcode> と \u003Ccode>rand.Intn(2) == 0\u003C\u002Fcode> を組み合わせる：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-go\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E06C75\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Slice\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">func\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">i\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">j\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\"> int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">bool\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\"> rand\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Intn\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">2\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">})\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>1回目：[46, 1, 55, 20, 11, 66, 56, 88, 80, 54, 41, 77, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>2回目：[68, 21, 62, 78, 20, 7, 3, 16, 72, 69, 85, 36, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>3回目：[26, 59, 90, 82, 20, 54, 79, 16, 43, 67, 77, 41, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>これもランダムな結果のようだ。\u003C\u002Fp>\n\u003Ch3 id=\"c-clang-22-1-8\">C++（clang 22.1.8）\u003C\u002Fh3>\n\u003Cp>次は C++ 版：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-cpp\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">std\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">begin\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(), \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">end\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(), [\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">&#x26;\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">gen\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">](\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> std::\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">uniform_int_distribution\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">&#x3C;>(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">)(gen) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">});\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>1回目：[73, 3, 10, 46, 88, 72, 26, 64, 38, 75, 91, 50, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>2回目：[20, 76, 33, 3, 86, 47, 34, 69, 94, 24, 71, 87, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>3回目：[95, 1, 22, 99, 21, 94, 6, 57, 51, 85, 10, 11, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>C++ は非常に素直に実行され、どのような入力でも処理が進む。\u003C\u002Fp>\n\u003Ch3 id=\"c-net-10-0-301\">C#（.NET 10.0.301）\u003C\u002Fh3>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-cs\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">Array\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, (\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">a\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">b\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) => \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Next\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">2\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> ? \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> : \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>1回目：[80, 57, 97, 92, 32, 3, 74, 10, 53, 35, 15, 29, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>2回目：[3, 27, 58, 98, 56, 28, 39, 82, 94, 53, 9, 50, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>3回目：[77, 19, 2, 61, 4, 15, 92, 95, 21, 7, 62, 84, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>しかし、配列サイズが N = 100000 に増えると、状況が変わります。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Unhandled exception. System.ArgumentException: Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: 'System.Comparison`1[System.Int32]'.\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>C# は配列がある程度のサイズに達すると、比較関数の不整合を検出できます。\u003C\u002Fp>\n\u003Ch3 id=\"java-jdk-26-0-1\">Java（JDK 26.0.1）\u003C\u002Fh3>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-java\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">((a, b) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">->\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\"> rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">nextInt\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">3\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>1回目：[0, 3, 8, 25, 5, 27, 20, 23, 9, 32, 18, 14, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>2回目：[25, 79, 1, 8, 14, 17, 2, 5, 16, 9, 3, 24, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>3回目：[50, 12, 0, 5, 11, 1, 22, 2, 3, 19, 16, 4, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>C# と同様に、配列サイズが N = 100000 に増えると、状況が変わります。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Exception in thread \"main\" java.lang.IllegalArgumentException: Comparison method violates its general contract!\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Java も配列がある程度のサイズに達すると、比較関数の問題を検出できます。\u003C\u002Fp>\n\u003Cp>このチェックは Java 7 から導入されました。Java 7 より前では、比較関数に欠陥があってもエラーは発生せず、単に順序が乱れた結果が返されていました。\u003C\u002Fp>\n\u003Ch3 id=\"rust-rustc-1-97-0\">Rust（rustc 1.97.0）\u003C\u002Fh3>\n\u003Cp>いよいよ Rust だ。AI は \u003Ccode>rand::Rng\u003C\u002Fcode> を使って \u003Ccode>Ordering::Less\u003C\u002Fcode> か \u003Ccode>Greater\u003C\u002Fcode> を決めるようにした：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-rust\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort_by\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">|\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">_\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">_\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">|\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">\tif\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\"> rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">gen\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">bool\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">>() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">\t\tOrdering\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">Less\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">\t} \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">else\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">\t\tOrdering\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">Greater\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">\t}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">});\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>1回目：thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>2回目：thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>3回目：thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>エラーメッセージ：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>user-provided comparison function does not correctly implement a total order\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>予想の範囲内ではあるが、\u003Cstrong>Rust は唯一直接 panic する言語だった\u003C\u002Fstrong>（ただし、コンパイル時にエラーになるという予想は外れた）。\u003C\u002Fp>\n\u003Cp>しかし、実行時に比較関数が total order（全順序）を実装しているかどうかを検出し、実装されていなければ即座に panic する。このように言語レベルで安全境界をチェックする姿勢は、私が持つ Rust のステレオタイプなイメージそのものだ。プログラムをクラッシュさせても、未定義動作を計算に参加させないという姿勢だ。\u003C\u002Fp>\n\u003Ch2 id=\"rust-本当\">Rust：だが、本当にそうなのか？\u003C\u002Fh2>\n\u003Cp>Rust をテストし終えた後、友人の \u003Ca href=\"https:\u002F\u002Flaunchpadx.top\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">LaunchPad\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa> と共有したところ、彼が Grok で再現を試みた際には panic が発生しなかった。\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-1.jpg\" alt=\"sorting-sins-grok-1\">\u003C\u002Fp>\n\u003Cp>これは不思議に思えた。なぜなら、私のローカル環境では確かに panic するからだ。\u003C\u002Fp>\n\u003Cp>そこで当然のようにバージョンの問題を疑い、Grok VM で使われている Rust のバージョンが \u003Ccode>1.75.0 (82e1608df 2023-12-21)\u003C\u002Fcode> であることを確認した。\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-2.jpg\" alt=\"sorting-sins-grok-2\">\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-3.jpg\" alt=\"sorting-sins-grok-3\">\u003C\u002Fp>\n\u003Cp>私のローカル環境のバージョンは \u003Ccode>1.97.0 (c980f4866 2026-06-30)\u003C\u002Fcode> だったので、ほぼ間違いなくバージョンが原因だと結論づけ、このブログ記事の初版を公開した。\u003C\u002Fp>\n\u003Cp>公開して間もなく、妹の \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Flfcypo\" target=\"_blank\" rel=\"noopener noreferrer\">lfcypo\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa> が歴史的なバージョンと具体的な変更点についての説明を提供してくれた。\u003C\u002Fp>\n\u003Cp>彼女の助けとその後の調査により、現在の振る舞いを形作ったのは以下の2つの重要な PR であることが分かった：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>PR\u003C\u002Fth>\n\u003Cth>PR 情報の概要\u003C\u002Fth>\n\u003Cth>マージ日\u003C\u002Fth>\n\u003Cth>リリースバージョン\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Frust-lang\u002Frust\u002Fpull\u002F124032\" target=\"_blank\" rel=\"noopener noreferrer\">#124032\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>ソート実装を置き換え—— \u003Cstrong>driftsort\u003C\u002Fstrong>（\u003Ccode>slice::sort\u003C\u002Fcode>）と \u003Cstrong>ipnsort\u003C\u002Fstrong>（\u003Ccode>slice::sort_unstable\u003C\u002Fcode>）を導入。新実装は \u003Cstrong>strict weak ordering\u003C\u002Fstrong> 違反を能動的に検出して panic する。旧来の Timsort\u002Fpdqsort では検出できなかった。\u003C\u002Ftd>\n\u003Ctd>2024-06-21\u003C\u002Ftd>\n\u003Ctd>Rust 1.81.0\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Frust-lang\u002Frust\u002Fpull\u002F128273\" target=\"_blank\" rel=\"noopener noreferrer\">#128273\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>Ord 違反のヘルプメッセージを改善—— panic メッセージを \u003Ccode>user-provided comparison function does not correctly implement a total order\u003C\u002Fcode> に最適化し、ドキュメントも改善。\u003C\u002Ftd>\n\u003Ctd>2024-08-11\u003C\u002Ftd>\n\u003Ctd>Rust 1.81.0\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>新しい実装では実行時整合性チェックが追加され、total order に違反する多くの比較関数に対して能動的に panic するようになった。一方、古いソート実装では通常そのような検出は行われない。\u003C\u002Fp>\n\u003Cp>1.81.0 より前（例：1.80.0）では、限定的なテストにおいて非決定的な比較関数を与えても、ソートは黙ってランダムに見える順列を出力した。1.81.0 以降では、高い確率で比較関数の不一致を検出し panic する。\u003C\u002Fp>\n\u003Ch2 id=\"\">まとめ\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Cstrong>言語\u003C\u002Fstrong>\u003C\u002Fth>\n\u003Cth>\u003Cstrong>振る舞い\u003C\u002Fstrong>\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>C++(Clang)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似的に）ランダム順列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Python\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似的に）ランダム順列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>JavaScript (Node.js \u002F Bun \u002F Deno)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似的に）ランダム順列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Go\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似的に）ランダム順列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Java\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>N=100 ランダム順列、N=1,000,000 \u003Cstrong>例外\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>C#\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>N=100 ランダム順列、N=1,000,000 \u003Cstrong>例外\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Rust(latest)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>panic\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Rust(v1.80.0)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似的に）ランダム順列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>5つの言語がランダム比較関数に対して「何事もなかったかのように振る舞う」のに対し、Rust の v1.81.0 以降だけが panic を選択し、Java と C# は配列がある程度のサイズに達するとエラーを投げる可能性がある。\u003C\u002Fp>\n\u003Cp>今後、各言語の実際の実行回数やソート結果に規則性があるかどうかを分析する予定だ。とりあえずはここまでにして、また時間があれば続ける。\u003C\u002Fp>\n",{"slug":5,"title":98,"lang":99,"desc":100,"date":9,"tags":101,"edit":9,"readingTime":102,"toc":107,"content":126},"一次突发奇想的「排序之罪」：如果对排序使用随机比较器","zh-Hans","如果我在排序的时候用一个随机函数做比较器，会发生什么？",[],{"text":103,"minutes":104,"time":105,"words":106},"10 min read",9.14,548400,1828,[108,110,112,114,115,116,117,118,119,120,121,124],{"level":18,"text":109,"slug":109},"突发奇想",{"level":18,"text":111,"slug":111},"对结果的猜想",{"level":18,"text":113,"slug":113},"各语言表现",{"level":28,"text":77,"slug":30},{"level":28,"text":79,"slug":33},{"level":28,"text":81,"slug":36},{"level":28,"text":83,"slug":39},{"level":28,"text":85,"slug":42},{"level":28,"text":87,"slug":45},{"level":28,"text":89,"slug":48},{"level":18,"text":122,"slug":123},"Rust：可是，当真如此吗？","rust-可是-当真如此吗",{"level":18,"text":125,"slug":125},"总结","\u003Ch2 id=\"突发奇想\">突发奇想\u003C\u002Fh2>\n\u003Cp>今天晚上或许是太无聊了，突发奇想到：\u003Cstrong>如果我在排序的时候用一个随机函数做比较器，会发生什么？\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>正常情况下，传递给 \u003Ccode>sort\u003C\u002Fcode> 的比较器应该返回 \u003Ccode>a &lt; b\u003C\u002Fcode> 的真假值，来决定谁排在前面。但假如我让它随机返回，那排序算法会有怎么样的行为呢？\u003C\u002Fp>\n\u003Cp>以及不同语言会怎么处理这种「违规」或者未定义行为？是给一个乱序结果，还是会直接报错？\u003C\u002Fp>\n\u003Cp>于是就有了这个仓库：\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FKuriyona\u002Fsorting-sins\" target=\"_blank\" rel=\"noopener noreferrer\">sorting-sins\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Fp>\n\u003Cp>本项目测试的语言有：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>JavaScript (Node.js &amp; Bun.js &amp; Deno)\u003C\u002Fli>\n\u003Cli>Python\u003C\u002Fli>\n\u003Cli>Go\u003C\u002Fli>\n\u003Cli>C++\u003C\u002Fli>\n\u003Cli>C#\u003C\u002Fli>\n\u003Cli>Java\u003C\u002Fli>\n\u003Cli>Rust (latest &amp; \u003Cstrong>v1.80.0\u003C\u002Fstrong>)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cblockquote>\n\u003Cp>对 JavaScript、Python、Go 的代码我是自己动手写的，其他几个语言是在 AI 的帮助下写的\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"对结果的猜想\">对结果的猜想\u003C\u002Fh2>\n\u003Cp>一开始，我觉得其他语言可能会给出一个类似随机的结果，或者说给出报错？\u003C\u002Fp>\n\u003Cp>不过我当时觉得，JavaScript 应该会给出一个随机的结果，或许 Rust 会在编译时报错，毕竟我对这两个语言的刻板印象就是如此（\u003C\u002Fp>\n\u003Ch2 id=\"各语言表现\">各语言表现\u003C\u002Fh2>\n\u003Ch3 id=\"javascript-node-js-v24-14-0-bun-js-1-3-14-deno-js-2-9-2\">JavaScript（Node.js v24.14.0 \u002F Bun.js 1.3.14 \u002F Deno.js 2.9.2）\u003C\u002Fh3>\n\u003Cp>用 \u003Ccode>Math.random() - 0.5\u003C\u002Fcode> 也就是 [-0.5, 0.5] 的随机浮点做比较器，跑三次：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-js\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(() \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">=>\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\"> Math\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">random\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">() \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0.5\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[67, 68, 87, 44, 50, 16, 42, 3, 24, 31, 1, 74, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[23, 69, 5, 47, 38, 36, 8, 27, 32, 11, 10, 0, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[50, 15, 7, 19, 34, 31, 32, 11, 47, 48, 51, 52, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>看起来是一个随机的结果，果不其然。\u003C\u002Fp>\n\u003Ch3 id=\"python-python-3-14-3\">Python（Python 3.14.3）\u003C\u002Fh3>\n\u003Cp>使用 \u003Ccode>functools.cmp_to_key\u003C\u002Fcode> 将比较函数转换为 key 函数。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-python\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">arr.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">key\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">cmp_to_key\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">lambda\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66;font-style:italic\"> a\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66;font-style:italic\">b\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">: random.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">choice\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">([\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">])))\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[79, 0, 44, 53, 38, 66, 90, 19, 3, 87, 31, 5, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[0, 80, 48, 22, 30, 79, 45, 95, 70, 84, 76, 74, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[17, 1, 73, 89, 80, 52, 26, 35, 30, 53, 9, 34, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>看起来 Python 也是给出一个随机的结果，倒是没有很意外。\u003C\u002Fp>\n\u003Ch3 id=\"go-go-1-26-2\">Go（go 1.26.2）\u003C\u002Fh3>\n\u003Cp>Go 语言我倒不是很熟，勉强写出了代码。\u003C\u002Fp>\n\u003Cp>用 \u003Ccode>sort.Slice\u003C\u002Fcode> 配合 \u003Ccode>rand.Intn(2) == 0\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-go\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E06C75\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Slice\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">func\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">i\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">j\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\"> int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">bool\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\"> rand\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Intn\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">2\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">})\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[46, 1, 55, 20, 11, 66, 56, 88, 80, 54, 41, 77, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[68, 21, 62, 78, 20, 7, 3, 16, 72, 69, 85, 36, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[26, 59, 90, 82, 20, 54, 79, 16, 43, 67, 77, 41, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>看起来依旧是一个类似随机的结果。\u003C\u002Fp>\n\u003Ch3 id=\"c-clang-22-1-8\">C++（clang 22.1.8）\u003C\u002Fh3>\n\u003Cp>接下来是 C++ 的版本：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-cpp\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">std\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">begin\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(), \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">end\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(), [\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">&#x26;\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">gen\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">](\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> std::\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">uniform_int_distribution\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">&#x3C;>(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">)(gen) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">});\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[73, 3, 10, 46, 88, 72, 26, 64, 38, 75, 91, 50, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[20, 76, 33, 3, 86, 47, 34, 69, 94, 24, 71, 87, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[95, 1, 22, 99, 21, 94, 6, 57, 51, 85, 10, 11, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>C++ 看起来很直接地运行了代码，不管如何输入，它都会执行下去。\u003C\u002Fp>\n\u003Ch3 id=\"c-net-10-0-301\">C#（.NET 10.0.301）\u003C\u002Fh3>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-cs\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">Array\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, (\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">a\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">b\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) => \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Next\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">2\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> ? \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> : \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[80, 57, 97, 92, 32, 3, 74, 10, 53, 35, 15, 29, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[3, 27, 58, 98, 56, 28, 39, 82, 94, 53, 9, 50, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[77, 19, 2, 61, 4, 15, 92, 95, 21, 7, 62, 84, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>然而当数量级增加至 N = 100000，情况就不同了。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Unhandled exception. System.ArgumentException: Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: 'System.Comparison`1[System.Int32]'.\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>C# 在数组长度达到一定规模后，才能检测出比较器的不一致问题。\u003C\u002Fp>\n\u003Ch3 id=\"java-jdk-26-0-1\">Java（JDK 26.0.1）\u003C\u002Fh3>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-java\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">((a, b) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">->\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\"> rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">nextInt\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">3\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[0, 3, 8, 25, 5, 27, 20, 23, 9, 32, 18, 14, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[25, 79, 1, 8, 14, 17, 2, 5, 16, 9, 3, 24, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[50, 12, 0, 5, 11, 1, 22, 2, 3, 19, 16, 4, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>和 C# 一样，当数量级增加至 N = 100000，情况就不同了。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Exception in thread \"main\" java.lang.IllegalArgumentException: Comparison method violates its general contract!\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Java 在数组长度达到一定规模后，同样能检测出比较器的问题。\u003C\u002Fp>\n\u003Cp>此检查自 Java 7 起引入。在 Java 7 之前，即便比较器有缺陷，Java 也不会报错，只会直接给出一个乱序的结果。\u003C\u002Fp>\n\u003Ch3 id=\"rust-rustc-1-97-0\">Rust（rustc 1.97.0）\u003C\u002Fh3>\n\u003Cp>终于到 Rust 了。AI 用了 \u003Ccode>rand::Rng\u003C\u002Fcode> 来决定 \u003Ccode>Ordering::Less\u003C\u002Fcode> 还是 \u003Ccode>Greater\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-rust\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort_by\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">|\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">_\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">_\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">|\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">\tif\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\"> rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">gen\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">bool\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">>() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">\t\tOrdering\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">Less\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">\t} \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">else\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">\t\tOrdering\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">Greater\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">\t}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">});\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>错误信息：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>user-provided comparison function does not correctly implement a total order\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>倒是在意料之中，\u003Cstrong>Rust 是唯一一个直接 panic 的语言\u003C\u002Fstrong>（虽然并没有如我所想的在编译时就报错）。\u003C\u002Fp>\n\u003Cp>但是它会在运行期检测你的比较器有没有实现 total order（全序），如果没有就当场 panic 给你看。这种在语言层面做安全边界检查的做法，确实是我对 Rust 刻板印象的一贯风格。宁可程序挂了，也不让未定义行为参与入程序运算。\u003C\u002Fp>\n\u003Ch2 id=\"rust-可是-当真如此吗\">Rust：可是，当真如此吗？\u003C\u002Fh2>\n\u003Cp>当我测试完 Rust 之后，与一个朋友 \u003Ca href=\"https:\u002F\u002Flaunchpadx.top\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">LaunchPad\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa> 分享的时候。对方尝试在 Grok 中复现时，发现并没有 panic。\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-1.jpg\" alt=\"sorting-sins-grok-1\">\u003C\u002Fp>\n\u003Cp>这让我很奇怪，因为我在本地运行时，确实会 panic。\u003C\u002Fp>\n\u003Cp>于是我们自然而然地怀疑到了版本的问题，是否是因为 Rust 版本的问题导致的？然后发现 Gork VM 中使用的 Rust 版本是 \u003Ccode>1.75.0 (82e1608df 2023-12-21)\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-2.jpg\" alt=\"sorting-sins-grok-2\">\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-3.jpg\" alt=\"sorting-sins-grok-3\">\u003C\u002Fp>\n\u003Cp>而我本地测试用的版本是 \u003Ccode>1.97.0 (c980f4866 2026-06-30)\u003C\u002Fcode>，我们自然而然就基本肯定了是版本的问题，不过我本来短期内也没打算先深入研究，发布了这篇博客的第一个版本。\u003C\u002Fp>\n\u003Cp>而发布出没多久，我的一个妹妹 \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Flfcypo\" target=\"_blank\" rel=\"noopener noreferrer\">lfcypo\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa> 便在后续提供了历史版本和具体变更的解释。\u003C\u002Fp>\n\u003Cp>经过她的帮助和后续研究，结论是两个关键 PR 塑造了当前行为：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>PR\u003C\u002Fth>\n\u003Cth>PR 信息摘要\u003C\u002Fth>\n\u003Cth>合并日期\u003C\u002Fth>\n\u003Cth>发布版本\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Frust-lang\u002Frust\u002Fpull\u002F124032\" target=\"_blank\" rel=\"noopener noreferrer\">#124032\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>替换排序实现——引入 \u003Cstrong>driftsort\u003C\u002Fstrong>（\u003Ccode>slice::sort\u003C\u002Fcode>）和 \u003Cstrong>ipnsort\u003C\u002Fstrong>（\u003Ccode>slice::sort_unstable\u003C\u002Fcode>）。新实现能主动检测 \u003Cstrong>strict weak ordering\u003C\u002Fstrong> 违例并 panic，旧版 Timsort\u002Fpdqsort 无法可靠检测。\u003C\u002Ftd>\n\u003Ctd>2024-06-21\u003C\u002Ftd>\n\u003Ctd>Rust 1.81.0\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Frust-lang\u002Frust\u002Fpull\u002F128273\" target=\"_blank\" rel=\"noopener noreferrer\">#128273\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>改进 Ord 违例帮助信息——将 panic 信息优化为 \u003Ccode>user-provided comparison function does not correctly implement a total order\u003C\u002Fcode>，并完善文档。\u003C\u002Ftd>\n\u003Ctd>2024-08-11\u003C\u002Ftd>\n\u003Ctd>Rust 1.81.0\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>新实现增加了运行时的一致性检查，对于许多违反 total order 的比较器会主动 panic，而旧版排序实现通常不会进行此类检测。\u003C\u002Fp>\n\u003Cp>在 1.81.0 之前（如 1.80.0）：在有限的测试中，传入非确定性比较器时，排序静默输出看似随机的排列。自 1.81.0 起：排序实现能以高概率检测到比较器不一致并 panic。\u003C\u002Fp>\n\u003Ch2 id=\"总结\">总结\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Cstrong>语言\u003C\u002Fstrong>\u003C\u002Fth>\n\u003Cth>\u003Cstrong>行为\u003C\u002Fstrong>\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>C++(Clang)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似）随机排列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Python\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似）随机排列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>JavaScript (Node.js \u002F Bun \u002F Deno)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似）随机排列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Go\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似）随机排列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Java\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>N=100 随机排列，N=1,000,000 \u003Cstrong>抛异常\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>C#\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>N=100 随机排列，N=1,000,000 \u003Cstrong>抛异常\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Rust(latest)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>panic\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Rust(v1.80.0)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似）随机排列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>5 种语言在随机比较器面前都选择了「当作啥都没发生」，而只有 Rust 的 v1.81.0 及以后版本选择了 panic，Java 和 C# 在数组长度达到一定规模后可能抛出错误。\u003C\u002Fp>\n\u003Cp>后续我将对各种语言的实际执行次数以及排序结果是否存在规律性进行分析，总之就先这样吧，有空再折腾。\u003C\u002Fp>\n",{"slug":5,"title":128,"lang":129,"desc":130,"date":9,"tags":131,"edit":9,"readingTime":132,"toc":133,"content":152},"一次突發奇想的「排序之罪」：如果對排序使用隨機比較器","zh-Hant","如果我在排序的時候用一個隨機函數做比較器，會發生什麼？",[],{"text":103,"minutes":104,"time":105,"words":106},[134,136,138,140,141,142,143,144,145,146,147,150],{"level":18,"text":135,"slug":135},"突發奇想",{"level":18,"text":137,"slug":137},"對結果的猜想",{"level":18,"text":139,"slug":139},"各語言表現",{"level":28,"text":77,"slug":30},{"level":28,"text":79,"slug":33},{"level":28,"text":81,"slug":36},{"level":28,"text":83,"slug":39},{"level":28,"text":85,"slug":42},{"level":28,"text":87,"slug":45},{"level":28,"text":89,"slug":48},{"level":18,"text":148,"slug":149},"Rust：可是，當真如此嗎？","rust-可是-當真如此嗎",{"level":18,"text":151,"slug":151},"總結","\u003Ch2 id=\"突發奇想\">突發奇想\u003C\u002Fh2>\n\u003Cp>今天晚上或許是太無聊了，突發奇想到：\u003Cstrong>如果我在排序的時候用一個隨機函數做比較器，會發生什麼？\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>正常情況下，傳遞給 \u003Ccode>sort\u003C\u002Fcode> 的比較器應該返回 \u003Ccode>a &lt; b\u003C\u002Fcode> 的真假值，來決定誰排在前面。但假如我讓它隨機返回，那排序算法會有怎麼樣的行爲呢？\u003C\u002Fp>\n\u003Cp>以及不同語言會怎麼處理這種「違規」或者未定義行爲？是給一個亂序結果，還是會直接報錯？\u003C\u002Fp>\n\u003Cp>於是就有了這個倉庫：\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FKuriyona\u002Fsorting-sins\" target=\"_blank\" rel=\"noopener noreferrer\">sorting-sins\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Fp>\n\u003Cp>本項目測試的語言有：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>JavaScript (Node.js &amp; Bun.js &amp; Deno)\u003C\u002Fli>\n\u003Cli>Python\u003C\u002Fli>\n\u003Cli>Go\u003C\u002Fli>\n\u003Cli>C++\u003C\u002Fli>\n\u003Cli>C#\u003C\u002Fli>\n\u003Cli>Java\u003C\u002Fli>\n\u003Cli>Rust (latest &amp; \u003Cstrong>v1.80.0\u003C\u002Fstrong>)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cblockquote>\n\u003Cp>對 JavaScript、Python、Go 的代碼我是自己動手寫的，其他幾個語言是在 AI 的幫助下寫的\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"對結果的猜想\">對結果的猜想\u003C\u002Fh2>\n\u003Cp>一開始，我覺得其他語言可能會給出一個類似隨機的結果，或者說給出報錯？\u003C\u002Fp>\n\u003Cp>不過我當時覺得，JavaScript 應該會給出一個隨機的結果，或許 Rust 會在編譯時報錯，畢竟我對這兩個語言的刻板印象就是如此（\u003C\u002Fp>\n\u003Ch2 id=\"各語言表現\">各語言表現\u003C\u002Fh2>\n\u003Ch3 id=\"javascript-node-js-v24-14-0-bun-js-1-3-14-deno-js-2-9-2\">JavaScript（Node.js v24.14.0 \u002F Bun.js 1.3.14 \u002F Deno.js 2.9.2）\u003C\u002Fh3>\n\u003Cp>用 \u003Ccode>Math.random() - 0.5\u003C\u002Fcode> 也就是 [-0.5, 0.5] 的隨機浮點做比較器，跑三次：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-js\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(() \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">=>\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\"> Math\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">random\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">() \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0.5\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[67, 68, 87, 44, 50, 16, 42, 3, 24, 31, 1, 74, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[23, 69, 5, 47, 38, 36, 8, 27, 32, 11, 10, 0, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[50, 15, 7, 19, 34, 31, 32, 11, 47, 48, 51, 52, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>看起來是一個隨機的結果，果不其然。\u003C\u002Fp>\n\u003Ch3 id=\"python-python-3-14-3\">Python（Python 3.14.3）\u003C\u002Fh3>\n\u003Cp>使用 \u003Ccode>functools.cmp_to_key\u003C\u002Fcode> 將比較函數轉換爲 key 函數。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-python\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">arr.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">key\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">=\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">cmp_to_key\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">lambda\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66;font-style:italic\"> a\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66;font-style:italic\">b\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">: random.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">choice\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">([\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">])))\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[79, 0, 44, 53, 38, 66, 90, 19, 3, 87, 31, 5, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[0, 80, 48, 22, 30, 79, 45, 95, 70, 84, 76, 74, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[17, 1, 73, 89, 80, 52, 26, 35, 30, 53, 9, 34, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>看起來 Python 也是給出一個隨機的結果，倒是沒有很意外。\u003C\u002Fp>\n\u003Ch3 id=\"go-go-1-26-2\">Go（go 1.26.2）\u003C\u002Fh3>\n\u003Cp>Go 語言我倒不是很熟，勉強寫出了代碼。\u003C\u002Fp>\n\u003Cp>用 \u003Ccode>sort.Slice\u003C\u002Fcode> 配合 \u003Ccode>rand.Intn(2) == 0\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-go\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E06C75\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Slice\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">func\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">i\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">j\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\"> int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">bool\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\"> rand\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Intn\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">2\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">})\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[46, 1, 55, 20, 11, 66, 56, 88, 80, 54, 41, 77, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[68, 21, 62, 78, 20, 7, 3, 16, 72, 69, 85, 36, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[26, 59, 90, 82, 20, 54, 79, 16, 43, 67, 77, 41, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>看起來依舊是一個類似隨機的結果。\u003C\u002Fp>\n\u003Ch3 id=\"c-clang-22-1-8\">C++（clang 22.1.8）\u003C\u002Fh3>\n\u003Cp>接下來是 C++ 的版本：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-cpp\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">std\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">begin\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(), \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">end\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(), [\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">&#x26;\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75;font-style:italic\">gen\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">](\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">int\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> std::\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">uniform_int_distribution\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">&#x3C;>(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">)(gen) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">});\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[73, 3, 10, 46, 88, 72, 26, 64, 38, 75, 91, 50, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[20, 76, 33, 3, 86, 47, 34, 69, 94, 24, 71, 87, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[95, 1, 22, 99, 21, 94, 6, 57, 51, 85, 10, 11, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>C++ 看起來很直接地運行了代碼，不管如何輸入，它都會執行下去。\u003C\u002Fp>\n\u003Ch3 id=\"c-net-10-0-301\">C#（.NET 10.0.301）\u003C\u002Fh3>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-cs\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">Array\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, (\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">a\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">b\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) => \u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">Next\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">2\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">==\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> ? \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> : \u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[80, 57, 97, 92, 32, 3, 74, 10, 53, 35, 15, 29, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[3, 27, 58, 98, 56, 28, 39, 82, 94, 53, 9, 50, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[77, 19, 2, 61, 4, 15, 92, 95, 21, 7, 62, 84, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>然而當數量級增加至 N = 100000，情況就不同了。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Unhandled exception. System.ArgumentException: Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: 'System.Comparison`1[System.Int32]'.\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>C# 在陣列長度達到一定規模後，才能檢測出比較器的不一致問題。\u003C\u002Fp>\n\u003Ch3 id=\"java-jdk-26-0-1\">Java（JDK 26.0.1）\u003C\u002Fh3>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-java\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">((a, b) \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">->\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\"> rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">nextInt\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\">3\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">) \u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">-\u003C\u002Fspan>\u003Cspan style=\"color:#D19A66\"> 1\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">);\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：[0, 3, 8, 25, 5, 27, 20, 23, 9, 32, 18, 14, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：[25, 79, 1, 8, 14, 17, 2, 5, 16, 9, 3, 24, ...]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：[50, 12, 0, 5, 11, 1, 22, 2, 3, 19, 16, 4, ...]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>和 C# 一樣，當數量級增加至 N = 100000，情況就不同了。\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>Exception in thread \"main\" java.lang.IllegalArgumentException: Comparison method violates its general contract!\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Java 在陣列長度達到一定規模後，同樣能檢測出比較器的問題。\u003C\u002Fp>\n\u003Cp>此檢查自 Java 7 起引入。在 Java 7 之前，即便比較器有缺陷，Java 也不會報錯，只會直接給出一個亂序的結果。\u003C\u002Fp>\n\u003Ch3 id=\"rust-rustc-1-97-0\">Rust（rustc 1.97.0）\u003C\u002Fh3>\n\u003Cp>終於到 Rust 了。AI 用了 \u003Ccode>rand::Rng\u003C\u002Fcode> 來決定 \u003Ccode>Ordering::Less\u003C\u002Fcode> 還是 \u003Ccode>Greater\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-rust\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#E06C75\">arr\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#61AFEF\">sort_by\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">|\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">_\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">, \u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\">_\u003C\u002Fspan>\u003Cspan style=\"color:#56B6C2\">|\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#C678DD\">\tif\u003C\u002Fspan>\u003Cspan style=\"color:#E06C75\"> rng\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">.\u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">gen\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">bool\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">>() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">\t\tOrdering\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">Less\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">\t} \u003C\u002Fspan>\u003Cspan style=\"color:#C678DD\">else\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E5C07B\">\t\tOrdering\u003C\u002Fspan>\u003Cspan style=\"color:#ABB2BF\">::\u003C\u002Fspan>\u003Cspan style=\"color:#E5C07B\">Greater\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">\t}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#ABB2BF\">});\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>第一次：thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第二次：thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>第三次：thread 'main' panicked at ...\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>錯誤信息：\u003C\u002Fp>\n\u003Cpre class=\"shiki one-dark-pro\" style=\"background-color:#282c34;color:#abb2bf\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>user-provided comparison function does not correctly implement a total order\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>倒是在意料之中，\u003Cstrong>Rust 是唯一一個直接 panic 的語言\u003C\u002Fstrong>（雖然並沒有如我所想的在編譯時就報錯）。\u003C\u002Fp>\n\u003Cp>但是它會在運行期檢測你的比較器有沒有實現 total order（全序），如果沒有就當場 panic 給你看。這種在語言層面做安全邊界檢查的做法，確實是我對 Rust 刻板印象的一貫風格。寧可程序掛了，也不讓未定義行爲參與入程序運算。\u003C\u002Fp>\n\u003Ch2 id=\"rust-可是-當真如此嗎\">Rust：可是，當真如此嗎？\u003C\u002Fh2>\n\u003Cp>當我測試完 Rust 之後，與一個朋友 \u003Ca href=\"https:\u002F\u002Flaunchpadx.top\u002F\" target=\"_blank\" rel=\"noopener noreferrer\">LaunchPad\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa> 分享的時候。對方嘗試在 Grok 中復現時，發現並沒有 panic。\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-1.jpg\" alt=\"sorting-sins-grok-1\">\u003C\u002Fp>\n\u003Cp>這讓我很奇怪，因爲我在本地運行時，確實會 panic。\u003C\u002Fp>\n\u003Cp>於是我們自然而然地懷疑到了版本的問題，是否是因爲 Rust 版本的問題導致的？然後發現 Gork VM 中使用的 Rust 版本是 \u003Ccode>1.75.0 (82e1608df 2023-12-21)\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-2.jpg\" alt=\"sorting-sins-grok-2\">\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fr2.kuriyona.com\u002Fstatic\u002F2026\u002F07\u002F15\u002Fsorting-sins-grok-3.jpg\" alt=\"sorting-sins-grok-3\">\u003C\u002Fp>\n\u003Cp>而我本地測試用的版本是 \u003Ccode>1.97.0 (c980f4866 2026-06-30)\u003C\u002Fcode>，我們自然而然就基本肯定了是版本的問題，不過我本來短期內也沒打算先深入研究，發佈了這篇博客的第一個版本。\u003C\u002Fp>\n\u003Cp>而發佈出沒多久，我的一個妹妹 \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Flfcypo\" target=\"_blank\" rel=\"noopener noreferrer\">lfcypo\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa> 便在後續提供了歷史版本和具體變更的解釋。\u003C\u002Fp>\n\u003Cp>經過她的幫助和後續研究，結論是兩個關鍵 PR 塑造了當前行爲：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>PR\u003C\u002Fth>\n\u003Cth>PR 信息摘要\u003C\u002Fth>\n\u003Cth>合併日期\u003C\u002Fth>\n\u003Cth>發佈版本\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Frust-lang\u002Frust\u002Fpull\u002F124032\" target=\"_blank\" rel=\"noopener noreferrer\">#124032\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>替換排序實現——引入 \u003Cstrong>driftsort\u003C\u002Fstrong>（\u003Ccode>slice::sort\u003C\u002Fcode>）和 \u003Cstrong>ipnsort\u003C\u002Fstrong>（\u003Ccode>slice::sort_unstable\u003C\u002Fcode>）。新實現能主動檢測 \u003Cstrong>strict weak ordering\u003C\u002Fstrong> 違例並 panic，舊版 Timsort\u002Fpdqsort 無法可靠檢測。\u003C\u002Ftd>\n\u003Ctd>2024-06-21\u003C\u002Ftd>\n\u003Ctd>Rust 1.81.0\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Frust-lang\u002Frust\u002Fpull\u002F128273\" target=\"_blank\" rel=\"noopener noreferrer\">#128273\u003Cspan class=\"material-symbols-outlined\" style=\"font-size:1em;vertical-align:middle;margin-left:2px;\">open_in_new\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>改進 Ord 違例幫助信息——將 panic 信息優化爲 \u003Ccode>user-provided comparison function does not correctly implement a total order\u003C\u002Fcode>，並完善文檔。\u003C\u002Ftd>\n\u003Ctd>2024-08-11\u003C\u002Ftd>\n\u003Ctd>Rust 1.81.0\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>新實現增加了運行時的一致性檢查，對於許多違反 total order 的比較器會主動 panic，而舊版排序實現通常不會進行此類檢測。\u003C\u002Fp>\n\u003Cp>在 1.81.0 之前（如 1.80.0）：在有限的測試中，傳入非確定性比較器時，排序靜默輸出看似隨機的排列。自 1.81.0 起：排序實現能以高概率檢測到比較器不一致並 panic。\u003C\u002Fp>\n\u003Ch2 id=\"總結\">總結\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Cstrong>語言\u003C\u002Fstrong>\u003C\u002Fth>\n\u003Cth>\u003Cstrong>行爲\u003C\u002Fstrong>\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>C++(Clang)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似）隨機排列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Python\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似）隨機排列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>JavaScript (Node.js \u002F Bun \u002F Deno)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似）隨機排列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Go\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似）隨機排列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Java\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>N=100 隨機排列，N=1,000,000 \u003Cstrong>拋異常\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>C#\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>N=100 隨機排列，N=1,000,000 \u003Cstrong>拋異常\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Rust(latest)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>panic\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Rust(v1.80.0)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>（疑似）隨機排列\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>5 種語言在隨機比較器面前都選擇了「當作啥都沒發生」，而只有 Rust 的 v1.81.0 及以後版本選擇了 panic，Java 和 C# 在陣列長度達到一定規模後可能拋出錯誤。\u003C\u002Fp>\n\u003Cp>後續我將對各種語言的實際執行次數以及排序結果是否存在規律性進行分析，總之就先這樣吧，有空再折騰。\u003C\u002Fp>\n",[154,157,160,163,166,169,172,175,178,181,184,187,190,193,196,199,202,205,208,211,214,217,220,223,226,229,232,235,238,241,244,247,250,253,256,259,262,265,268,271,274,277,280,283,286,289,292,295,298,301,304,307,310,313,316,319,322,325,328,331,334,337,340,343,346,349,352,355,358,361,364,367,370,373,376,379,382,385,388,391,394,397,400,403,406,409,412,415,418,421,424,427,430,433,436,439,442,445,448,451,454,457,460,463,466,469,472,475,478,481,484,487,490,493,496,499,502,505,508,511,514,517,520,523,526,529,532,535,538,541,544,547,550,553,556,559,562,565,568,571,574,577,580,583,586,589,592,595,598,601,604,607,610,613,616,619,622,625,628,631,634,637,640,643,646,649,652,655,658,661,664,667,670,673,676,679,682,685,688,691,694,697,700,703,706,709,712,715,718,721,724,727,730,733,736,739,742,745,748,751,754,757,760,763,766,769,772,775,778,781,784,787,790,793,796,799,802,805,808,811,814,817,820,823,826,829,832,835,838,841,844,847,850,853,856,859,862,865,868,871,874,877,880,883,886,889,892,895,898,901,904,907,910,913,916,919,922,925,928,931,934,937,940,943,946,949,952,955,958,961,964,967,970,973,976,979,982,985,988,991,994,997,1000,1003,1006,1009,1012,1015,1018,1021,1024,1027,1030,1033,1036,1039,1042,1045,1048,1051,1054,1057,1060,1063,1066,1069,1072,1075,1078,1081,1084,1087,1090,1093,1096],{"name":155,"code":156},"Amsterdam, Netherlands - (AMS)","AMS",{"name":158,"code":159},"Amman, Jordan - (AMM)","AMM",{"name":161,"code":162},"Adelaide, SA, Australia - (ADL)","ADL",{"name":164,"code":165},"Americana, Brazil - (QWJ)","QWJ",{"name":167,"code":168},"Accra, Ghana - (ACC)","ACC",{"name":170,"code":171},"Ahmedabad, India - (AMD)","AMD",{"name":173,"code":174},"Albuquerque, NM, United States - (ABQ)","ABQ",{"name":176,"code":177},"Auckland, New Zealand - (AKL)","AKL",{"name":179,"code":180},"Athens, Greece - (ATH)","ATH",{"name":182,"code":183},"Arica, Chile - (ARI)","ARI",{"name":185,"code":186},"Algiers, Algeria - (ALG)","ALG",{"name":188,"code":189},"Almaty, Kazakhstan - (ALA)","ALA",{"name":191,"code":192},"Astara, Azerbaijan - (LLK)","LLK",{"name":194,"code":195},"Anchorage, AK, United States - (ANC)","ANC",{"name":197,"code":198},"Ashburn, VA, United States - (IAD)","IAD",{"name":200,"code":201},"Brisbane, QLD, Australia - (BNE)","BNE",{"name":203,"code":204},"Barcelona, Spain - (BCN)","BCN",{"name":206,"code":207},"Baghdad, Iraq - (BGW)","BGW",{"name":209,"code":210},"Annaba, Algeria - (AAE)","AAE",{"name":212,"code":213},"Atlanta, GA, United States - (ATL)","ATL",{"name":215,"code":216},"Belgrade, Serbia - (BEG)","BEG",{"name":218,"code":219},"Baku, Azerbaijan - (GYD)","GYD",{"name":221,"code":222},"Bangalore, India - (BLR)","BLR",{"name":224,"code":225},"Antananarivo, Madagascar - (TNR)","TNR",{"name":227,"code":228},"Canberra, ACT, Australia - (CBR)","CBR",{"name":230,"code":231},"Berlin, Germany - (TXL)","TXL",{"name":233,"code":234},"Bangkok, Thailand - (BKK)","BKK",{"name":236,"code":237},"Cape Town, South Africa - (CPT)","CPT",{"name":239,"code":240},"Belo Horizonte, Brazil - (CNF)","CNF",{"name":242,"code":243},"Basra, Iraq - (BSR)","BSR",{"name":245,"code":246},"Christchurch, New Zealand - (CHC)","CHC",{"name":248,"code":249},"Austin, TX, United States - (AUS)","AUS",{"name":251,"code":252},"Beirut, Lebanon - (BEY)","BEY",{"name":254,"code":255},"Bandar Seri Begawan, Brunei - (BWN)","BWN",{"name":257,"code":258},"Hagatna, Guam - (GUM)","GUM",{"name":260,"code":261},"Bangor, ME, United States - (BGR)","BGR",{"name":263,"code":264},"Bordeaux, France - (BOD)","BOD",{"name":266,"code":267},"Boston, MA, United States - (BOS)","BOS",{"name":269,"code":270},"Dakar, Senegal - (DKR)","DKR",{"name":272,"code":273},"Dammam, Saudi Arabia - (DMM)","DMM",{"name":275,"code":276},"Bratislava, Slovakia   - (BTS)","BTS",{"name":278,"code":279},"Melbourne, VIC, Australia - (MEL)","MEL",{"name":281,"code":282},"Doha, Qatar - (DOH)","DOH",{"name":284,"code":285},"Brussels, Belgium - (BRU)","BRU",{"name":287,"code":288},"Buffalo, NY, United States - (BUF)","BUF",{"name":290,"code":291},"Dar Es Salaam, Tanzania - (DAR)","DAR",{"name":293,"code":294},"Brasilia, Brazil - (BSB)","BSB",{"name":296,"code":297},"Buenos Aires, Argentina - (EZE)","EZE",{"name":299,"code":300},"Bucharest, Romania - (OTP)","OTP",{"name":302,"code":303},"Dubai, United Arab Emirates - (DXB)","DXB",{"name":305,"code":306},"Djibouti City, Djibouti - (JIB)","JIB",{"name":308,"code":309},"Calgary, AB, Canada - (YYC)","YYC",{"name":311,"code":312},"Cebu, Philippines - (CEB)","CEB",{"name":314,"code":315},"Noumea, New Caledonia - (NOU)","NOU",{"name":317,"code":318},"Perth, WA, Australia - (PER)","PER",{"name":320,"code":321},"Budapest, Hungary - (BUD)","BUD",{"name":323,"code":324},"Durban, South Africa - (DUR)","DUR",{"name":326,"code":327},"Charlotte, NC, United States - (CLT)","CLT",{"name":329,"code":330},"Erbil, Iraq - (EBL)","EBL",{"name":332,"code":333},"Chandigarh, India - (IXC)","IXC",{"name":335,"code":336},"Chicago, IL, United States - (ORD)","ORD",{"name":338,"code":339},"Sydney, NSW, Australia - (SYD)","SYD",{"name":341,"code":342},"Campinas, Brazil - (VCP)","VCP",{"name":344,"code":345},"Changde, China - (CGD)","CGD",{"name":347,"code":348},"Haifa, Israel - (HFA)","HFA",{"name":350,"code":351},"Gaborone, Botswana - (GBE)","GBE",{"name":353,"code":354},"Copenhagen, Denmark - (CPH)","CPH",{"name":356,"code":357},"Chennai, India - (MAA)","MAA",{"name":359,"code":360},"Harare, Zimbabwe - (HRE)","HRE",{"name":362,"code":363},"Jeddah, Saudi Arabia - (JED)","JED",{"name":365,"code":366},"Tahiti, French Polynesia - (PPT)","PPT",{"name":368,"code":369},"Cleveland, OH, United States - (CLE)","CLE",{"name":371,"code":372},"Johannesburg, South Africa - (JNB)","JNB",{"name":374,"code":375},"Kuwait City, Kuwait - (KWI)","KWI",{"name":377,"code":378},"Dublin, Ireland - (DUB)","DUB",{"name":380,"code":381},"Columbus, OH, United States - (CMH)","CMH",{"name":383,"code":384},"Suva, Fiji - (SUV)","SUV",{"name":386,"code":387},"Dallas, TX, United States - (DFW)","DFW",{"name":389,"code":390},"Manama, Bahrain - (BAH)","BAH",{"name":392,"code":393},"Kigali, Rwanda - (KGL)","KGL",{"name":395,"code":396},"Chittagong, Bangladesh - (CGP)","CGP",{"name":398,"code":399},"Hobart, Australia - (HBA)","HBA",{"name":401,"code":402},"Frankfurt, Germany - (FRA)","FRA",{"name":404,"code":405},"Muscat, Oman - (MCT)","MCT",{"name":407,"code":408},"Denver, CO, United States - (DEN)","DEN",{"name":410,"code":411},"Colombo, Sri Lanka - (CMB)","CMB",{"name":413,"code":414},"Lagos, Nigeria - (LOS)","LOS",{"name":416,"code":417},"Curitiba, Brazil - (CWB)","CWB",{"name":419,"code":420},"Wellington, New Zealand - (WLG)","WLG",{"name":422,"code":423},"Luanda, Angola - (LAD)","LAD",{"name":425,"code":426},"Detroit, MI, United States - (DTW)","DTW",{"name":428,"code":429},"Geneva, Switzerland - (GVA)","GVA",{"name":431,"code":432},"Dhaka, Bangladesh - (DAC)","DAC",{"name":434,"code":435},"Florianopolis, Brazil - (FLN)","FLN",{"name":437,"code":438},"Najaf, Iraq - (NJF)","NJF",{"name":440,"code":441},"Fortaleza, Brazil - (FOR)","FOR",{"name":443,"code":444},"Gothenburg, Sweden - (GOT)","GOT",{"name":446,"code":447},"Maputo, Mozambique - (MPM)","MPM",{"name":449,"code":450},"Foshan, China - (FUO)","FUO",{"name":452,"code":453},"Nasiriyah, Iraq - (XNH)","XNH",{"name":455,"code":456},"Durham, NC, United States - (RDU)","RDU",{"name":458,"code":459},"Mombasa, Kenya - (MBA)","MBA",{"name":461,"code":462},"Hamburg, Germany - (HAM)","HAM",{"name":464,"code":465},"Ramallah - (ZDM)","ZDM",{"name":467,"code":468},"Georgetown, Guyana - (GEO)","GEO",{"name":470,"code":471},"Fukuoka, Japan - (FUK)","FUK",{"name":473,"code":474},"Guadalajara, Mexico - (GDL)","GDL",{"name":476,"code":477},"Helsinki, Finland - (HEL)","HEL",{"name":479,"code":480},"Riyadh, Saudi Arabia - (RUH)","RUH",{"name":482,"code":483},"Nairobi, Kenya - (NBO)","NBO",{"name":485,"code":486},"Fuzhou, China - (FOC)","FOC",{"name":488,"code":489},"Halifax, Canada - (YHZ)","YHZ",{"name":491,"code":492},"Guangzhou, China - (CAN)","CAN",{"name":494,"code":495},"Istanbul, Turkey - (IST)","IST",{"name":497,"code":498},"Guatemala City, Guatemala - (GUA)","GUA",{"name":500,"code":501},"Honolulu, HI, United States - (HNL)","HNL",{"name":503,"code":504},"Sulaymaniyah, Iraq - (ISU)","ISU",{"name":506,"code":507},"Oran, Algeria - (ORN)","ORN",{"name":509,"code":510},"Tel Aviv, Israel - (TLV)","TLV",{"name":512,"code":513},"Houston, TX, United States - (IAH)","IAH",{"name":515,"code":516},"Guayaquil, Ecuador - (GYE)","GYE",{"name":518,"code":519},"Haikou, China - (HAK)","HAK",{"name":521,"code":522},"Ouagadougou, Burkina Faso   - (OUA)","OUA",{"name":524,"code":525},"Izmir, Turkey - (ADB)","ADB",{"name":527,"code":528},"Kyiv, Ukraine - (KBP)","KBP",{"name":530,"code":531},"Port Louis, Mauritius - (MRU)","MRU",{"name":533,"code":534},"Indianapolis, IN, United States - (IND)","IND",{"name":536,"code":537},"Hanoi, Vietnam - (HAN)","HAN",{"name":539,"code":540},"Lisbon, Portugal - (LIS)","LIS",{"name":542,"code":543},"Jacksonville, FL, United States - (JAX)","JAX",{"name":545,"code":546},"Hengshui, China - (SJW)","SJW",{"name":548,"code":549},"Joinville, Brazil - (JOI)","JOI",{"name":551,"code":552},"London, United Kingdom - (LHR)","LHR",{"name":554,"code":555},"Kansas City, MO, United States - (MCI)","MCI",{"name":557,"code":558},"Ho Chi Minh City, Vietnam - (SGN)","SGN",{"name":560,"code":561},"Tunis, Tunisia - (TUN)","TUN",{"name":563,"code":564},"Juazeiro do Norte, Brazil - (JDO)","JDO",{"name":566,"code":567},"Hong Kong - (HKG)","HKG",{"name":569,"code":570},"Lima, Peru - (LIM)","LIM",{"name":572,"code":573},"Luxembourg City, Luxembourg - (LUX)","LUX",{"name":575,"code":576},"Kingston, Jamaica - (KIN)","KIN",{"name":578,"code":579},"Kinshasa, DR Congo - (FIH)","FIH",{"name":581,"code":582},"Las Vegas, NV, United States - (LAS)","LAS",{"name":584,"code":585},"Hyderabad, India - (HYD)","HYD",{"name":587,"code":588},"Manaus, Brazil - (MAO)","MAO",{"name":590,"code":591},"Lyon, France - (LYS)","LYS",{"name":593,"code":594},"Cairo, Egypt - (CAI)","CAI",{"name":596,"code":597},"Los Angeles, CA, United States - (LAX)","LAX",{"name":599,"code":600},"Madrid, Spain - (MAD)","MAD",{"name":602,"code":603},"Islamabad, Pakistan - (ISB)","ISB",{"name":605,"code":606},"Windhoek, Namibia - (WDH)","WDH",{"name":608,"code":609},"Manchester, United Kingdom - (MAN)","MAN",{"name":611,"code":612},"Memphis, TN, United States - (MEM)","MEM",{"name":614,"code":615},"Jakarta, Indonesia - (CGK)","CGK",{"name":617,"code":618},"Yamoussoukro, Ivory Coast - (ASK)","ASK",{"name":620,"code":621},"Marseille, France - (MRS)","MRS",{"name":623,"code":624},"Panama City, Panama - (PTY)","PTY",{"name":626,"code":627},"Mexico City, Mexico - (MEX)","MEX",{"name":629,"code":630},"Abidjan, Ivory Coast - (ABJ)","ABJ",{"name":632,"code":633},"Miami, FL, United States - (MIA)","MIA",{"name":635,"code":636},"Milan, Italy - (MXP)","MXP",{"name":638,"code":639},"Jinan, China - (TNA)","TNA",{"name":641,"code":642},"Paramaribo, Suriname - (PBM)","PBM",{"name":644,"code":645},"Kampala, Uganda - (EBB)","EBB",{"name":647,"code":648},"Minneapolis, MN, United States - (MSP)","MSP",{"name":650,"code":651},"Porto Alegre, Brazil - (POA)","POA",{"name":653,"code":654},"Minsk, Belarus - (MSQ)","MSQ",{"name":656,"code":657},"Moscow, Russia - (DME)","DME",{"name":659,"code":660},"Johor Bahru, Malaysia - (JHB)","JHB",{"name":662,"code":663},"Quito, Ecuador - (UIO)","UIO",{"name":665,"code":666},"Munich, Germany - (MUC)","MUC",{"name":668,"code":669},"Kanpur, India - (KNU)","KNU",{"name":671,"code":672},"Nashville, United States - (BNA)","BNA",{"name":674,"code":675},"Newark, NJ, United States - (EWR)","EWR",{"name":677,"code":678},"Nicosia, Cyprus - (LCA)","LCA",{"name":680,"code":681},"Kaohsiung City - (KHH)","KHH",{"name":683,"code":684},"Recife, Brazil - (REC)","REC",{"name":686,"code":687},"Oslo, Norway - (OSL)","OSL",{"name":689,"code":690},"Norfolk, VA, United States - (ORF)","ORF",{"name":692,"code":693},"Karachi, Pakistan - (KHI)","KHI",{"name":695,"code":696},"Ribeirao Preto, Brazil - (RAO)","RAO",{"name":698,"code":699},"Rio de Janeiro, Brazil - (GIG)","GIG",{"name":701,"code":702},"Kathmandu, Nepal - (KTM)","KTM",{"name":704,"code":705},"Palermo, Italy - (PMO)","PMO",{"name":707,"code":708},"Oklahoma City, OK, United States - (OKC)","OKC",{"name":710,"code":711},"Paris, France - (CDG)","CDG",{"name":713,"code":714},"Omaha, NE, United States - (OMA)","OMA",{"name":716,"code":717},"Prague, Czech Republic - (PRG)","PRG",{"name":719,"code":720},"Kolkata, India - (CCU)","CCU",{"name":722,"code":723},"Santiago, Chile - (SCL)","SCL",{"name":725,"code":726},"Philadelphia, United States - (PHL)","PHL",{"name":728,"code":729},"Krasnoyarsk, Russia - (KJA)","KJA",{"name":731,"code":732},"Lusaka, Zambia - (LUN)","LUN",{"name":734,"code":735},"Kuala Lumpur, Malaysia - (KUL)","KUL",{"name":737,"code":738},"Phoenix, AZ, United States - (PHX)","PHX",{"name":740,"code":741},"Riga, Latvia - (RIX)","RIX",{"name":743,"code":744},"Santo Domingo, Dominican Republic - (SDQ)","SDQ",{"name":746,"code":747},"Addis Ababa, Ethiopia - (ADD)","ADD",{"name":749,"code":750},"Rome, Italy - (FCO)","FCO",{"name":752,"code":753},"Pittsburgh, PA, United States - (PIT)","PIT",{"name":755,"code":756},"Lilongwe, Malawi - (LLW)","LLW",{"name":758,"code":759},"Portland, OR, United States - (PDX)","PDX",{"name":761,"code":762},"Saint Petersburg, Russia - (LED)","LED",{"name":764,"code":765},"Langfang, China - (PKX)","PKX",{"name":767,"code":768},"Constantine, Algeria - (CZL)","CZL",{"name":770,"code":771},"Queretaro, MX, Mexico - (QRO)","QRO",{"name":773,"code":774},"Santa Venera, Malta - (MLA)","MLA",{"name":776,"code":777},"Douala, Cameroon - (DLA)","DLA",{"name":779,"code":780},"Richmond, VA, United States - (RIC)","RIC",{"name":782,"code":783},"Sorocaba, Brazil - (SOD)","SOD",{"name":785,"code":786},"Skopje, North Macedonia - (SKP)","SKP",{"name":788,"code":789},"Sofia, Bulgaria - (SOF)","SOF",{"name":791,"code":792},"Macau - (MFM)","MFM",{"name":794,"code":795},"Sacramento, CA, United States - (SMF)","SMF",{"name":797,"code":798},"Stockholm, Sweden - (ARN)","ARN",{"name":800,"code":801},"Salt Lake City, UT, United States - (SLC)","SLC",{"name":803,"code":804},"Tegucigalpa, Honduras - (TGU)","TGU",{"name":806,"code":807},"Stuttgart, Germany   - (STR)","STR",{"name":809,"code":810},"San Antonio, TX, United States - (SAT)","SAT",{"name":812,"code":813},"Manila, Philippines - (MNL)","MNL",{"name":815,"code":816},"San Diego, CA, United States - (SAN)","SAN",{"name":818,"code":819},"Tallinn, Estonia - (TLL)","TLL",{"name":821,"code":822},"Mumbai, India - (BOM)","BOM",{"name":824,"code":825},"Tbilisi, Georgia - (TBS)","TBS",{"name":827,"code":828},"San Francisco, CA, United States - (SFO)","SFO",{"name":830,"code":831},"Vitoria, Brazil - (VIX)","VIX",{"name":833,"code":834},"San Jose, CA, United States - (SJC)","SJC",{"name":836,"code":837},"Thessaloniki, Greece - (SKG)","SKG",{"name":839,"code":840},"Nagpur, India - (NAG)","NAG",{"name":842,"code":843},"Saskatoon, SK, Canada - (YXE)","YXE",{"name":845,"code":846},"Naha, Japan  - (OKA)","OKA",{"name":848,"code":849},"Tirana, Albania - (TIA)","TIA",{"name":851,"code":852},"Campos dos Goytacazes, Brazil - (CAW)","CAW",{"name":854,"code":855},"Seattle, WA, United States - (SEA)","SEA",{"name":857,"code":858},"Vienna, Austria - (VIE)","VIE",{"name":860,"code":861},"New Delhi, India - (DEL)","DEL",{"name":863,"code":864},"Chapeco, Brazil - (XAP)","XAP",{"name":866,"code":867},"Osaka, Japan - (KIX)","KIX",{"name":869,"code":870},"Vilnius, Lithuania - (VNO)","VNO",{"name":872,"code":873},"Sioux Falls, SD, United States - (FSD)","FSD",{"name":875,"code":876},"Bridgetown, Barbados - (BGI)","BGI",{"name":878,"code":879},"Warsaw, Poland - (WAW)","WAW",{"name":881,"code":882},"Patna, India - (PAT)","PAT",{"name":884,"code":885},"Phnom Penh, Cambodia - (PNH)","PNH",{"name":887,"code":888},"Tallahassee, FL, United States - (TLH)","TLH",{"name":890,"code":891},"Santiago de los Caballeros, Dominican Republic - (STI)","STI",{"name":893,"code":894},"Wroclaw, Poland - (WRO)","WRO",{"name":896,"code":897},"Zagreb, Croatia - (ZAG)","ZAG",{"name":899,"code":900},"Qingdao, China - (TAO)","TAO",{"name":902,"code":903},"Tampa, FL, United States - (TPA)","TPA",{"name":905,"code":906},"La Paz, Bolivia - (LPB)","LPB",{"name":908,"code":909},"Toronto, ON, Canada - (YYZ)","YYZ",{"name":911,"code":912},"Seoul, South Korea - (ICN)","ICN",{"name":914,"code":915},"Zurich, Switzerland - (ZRH)","ZRH",{"name":917,"code":918},"Vancouver, BC, Canada - (YVR)","YVR",{"name":920,"code":921},"Shanghai, China - (SHA)","SHA",{"name":923,"code":924},"San Juan, Puerto Rico - (SJU)","SJU",{"name":926,"code":927},"Singapore, Singapore - (SIN)","SIN",{"name":929,"code":930},"Winnipeg, MB, Canada - (YWG)","YWG",{"name":932,"code":933},"Barranquilla, Colombia - (BAQ)","BAQ",{"name":935,"code":936},"Surat Thani, Thailand - (URT)","URT",{"name":938,"code":939},"Palmas, Brazil - (PMW)","PMW",{"name":941,"code":942},"Cincinnati, United States - (CVG)","CVG",{"name":944,"code":945},"Taipei - (TPE)","TPE",{"name":947,"code":948},"Aracatuba, Brazil - (ARU)","ARU",{"name":950,"code":951},"Port of Spain, Trinidad and Tobago - (POS)","POS",{"name":953,"code":954},"Salvador, Brazil - (SSA)","SSA",{"name":956,"code":957},"Tokyo, Japan - (NRT)","NRT",{"name":959,"code":960},"Ulaanbaatar, Mongolia - (ULN)","ULN",{"name":962,"code":963},"Vientiane, Laos - (VTE)","VTE",{"name":965,"code":966},"Xinyu, China - (KHN)","KHN",{"name":968,"code":969},"Yerevan, Armenia - (EVN)","EVN",{"name":971,"code":972},"Yogyakarta, Indonesia - (JOG)","JOG",{"name":974,"code":975},"Cagayan de Oro, Philippines - (CGY)","CGY",{"name":977,"code":978},"Ljubljana, Slovenia - (LJU)","LJU",{"name":980,"code":981},"Kochi, India - (COK)","COK",{"name":983,"code":984},"Cuiaba, Brazil - (CGB)","CGB",{"name":986,"code":987},"Denpasar, Indonesia - (DPS)","DPS",{"name":989,"code":990},"Kannur, India - (CNN)","CNN",{"name":992,"code":993},"Cali, Colombia - (CLO)","CLO",{"name":995,"code":996},"San Pedro Sula, Honduras - (SAP)","SAP",{"name":998,"code":999},"Shenzhen, China - (SZX)","SZX",{"name":1001,"code":1002},"Guiyang, China - (KWE)","KWE",{"name":1004,"code":1005},"Shaoxing, China - (HGH)","HGH",{"name":1007,"code":1008},"Changzhou, China - (CZX)","CZX",{"name":1010,"code":1011},"Kunming, China - (KMG)","KMG",{"name":1013,"code":1014},"Chiang Mai, Thailand - (CNX)","CNX",{"name":1016,"code":1017},"Zhengzhou, China - (CGO)","CGO",{"name":1019,"code":1020},"Yangquan, China - (TYN)","TYN",{"name":1022,"code":1023},"Changsha, China - (CSX)","CSX",{"name":1025,"code":1026},"Dalian, China - (DLC)","DLC",{"name":1028,"code":1029},"Beihai, China - (BHY)","BHY",{"name":1031,"code":1032},"Chongqing, China - (CKG)","CKG",{"name":1034,"code":1035},"Xiangyang, China - (XFN)","XFN",{"name":1037,"code":1038},"Da Nang, Vietnam - (DAD)","DAD",{"name":1040,"code":1041},"Jiaxing, China - (JXG)","JXG",{"name":1043,"code":1044},"Tarlac City, Philippines - (CRK)","CRK",{"name":1046,"code":1047},"Thimphu, Bhutan - (PBH)","PBH",{"name":1049,"code":1050},"Baoji, China - (XIY)","XIY",{"name":1052,"code":1053},"Astana, Kazakhstan - (NQZ)","NQZ",{"name":1055,"code":1056},"Kuching, Malaysia - (KCH)","KCH",{"name":1058,"code":1059},"Aktobe, Kazakhstan - (AKX)","AKX",{"name":1061,"code":1062},"Tongren, China - (TEN)","TEN",{"name":1064,"code":1065},"Taizhou, China - (HYN)","HYN",{"name":1067,"code":1068},"Bishkek, Kyrgyzstan - (FRU)","FRU",{"name":1070,"code":1071},"Malang, Indonesia - (MLG)","MLG",{"name":1073,"code":1074},"Lahore, Pakistan - (LHE)","LHE",{"name":1076,"code":1077},"Chengdu, China - (CTU)","CTU",{"name":1079,"code":1080},"Agra, India - (AGR)","AGR",{"name":1082,"code":1083},"Coimbatore, India - (CJB)","CJB",{"name":1085,"code":1086},"Xingyi, China - (ACX)","ACX",{"name":1088,"code":1089},"Bhubaneswar, India - (BBI)","BBI",{"name":1091,"code":1092},"Luoyang, China - (LYA)","LYA",{"name":1094,"code":1095},"Pune, India - (PNQ)","PNQ",{"name":1097,"code":1098},"Sambalpur, India - (JRG)","JRG",1784493041835]