function quickSortInPlace(arr, left = 0, right = arr.length - 1) {
if (left >= right) return;
const pivotIndex = partition(arr, left, right);
quickSortInPlace(arr, left, pivotIndex - 1);
quickSortInPlace(arr, pivotIndex + 1, right);
}
function partition(arr, left, right) {
const pivot = arr[right];
let i = left;
for (let j = left; j < right; j++) {
if (arr[j] < pivot) {
[arr[i], arr[j]] = [arr[j], arr[i]];
i++;
}
}
[arr[i], arr[right]] = [arr[right], arr[i]];
return i;
}
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[arr.length - 1];
const left = [];
const right = [];
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
// =============
function quickSortGPT(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('quickSort expects an array');
}
if (arr.length <= 1) return [...arr];
const pivot = arr[Math.floor(arr.length / 2)];
const left = [];
const equal = [];
const right = [];
for (const el of arr) {
if (el < pivot) left.push(el);
else if (el > pivot) right.push(el);
else equal.push(el);
}
return [...quickSortGPT(left), ...equal, ...quickSortGPT(right)];
}
function quickSortInPlaceGPT(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('quickSortInPlace expects an array');
}
const stack = [[0, arr.length - 1]];
while (stack.length) {
const [lo, hi] = stack.pop();
if (lo >= hi) continue;
const pivotIndex = partitionGPT(arr, lo, hi);
// Tail‑recursion elimination: push larger partition first
if (pivotIndex - 1 - lo > hi - (pivotIndex + 1)) {
stack.push([lo, pivotIndex - 1]);
stack.push([pivotIndex + 1, hi]);
} else {
stack.push([pivotIndex + 1, hi]);
stack.push([lo, pivotIndex - 1]);
}
}
return arr;
}
function medianOfThreeGPT(a, b, c) {
return (a - b) * (c - a) >= 0 ? a
: (b - a) * (c - b) >= 0 ? b
: c;
}
function partitionGPT(arr, lo, hi) {
const mid = lo + ((hi - lo) >> 1);
const pivotValue = medianOfThreeGPT(arr[lo], arr[mid], arr[hi]);
while (true) {
while (arr[lo] < pivotValue) lo++;
while (arr[hi] > pivotValue) hi--;
if (lo >= hi) return hi;
[arr[lo], arr[hi]] = [arr[hi], arr[lo]];
lo++;
hi--;
}
}
function testQuicksort(qs, qsp) {
const repeat = 100;
const arrLength = 100000;
const unsortedArray = new Array();
for (let i = 0; i < arrLength; i++)
unsortedArray.push(Math.round(Math.random() * arrLength));
let sorted = [];
const qb = performance.now();
for (let i = 0; i < repeat; i++)
sorted = qs(unsortedArray);
const qe = performance.now();
const rqb = performance.now();
for (let i = 0; i < repeat; i++) {
let copied = [...unsortedArray];
qsp(copied);
}
const rqe = performance.now();
// 小数点以下2桁まで
const p1 = ((qe - qb) / repeat).toFixed(2);
const p2 = ((rqe - rqb) / repeat).toFixed(2);
console.log(`Quicksort: ${p1} ms, In-place: ${p2} ms`);
}
function main() {
const useGPT = process.argv.includes('--gpt');
console.log(`Using ${useGPT ? 'GPT' : 'geekNews'} quicksort implementation.`);
if (useGPT) {
testQuicksort(quickSortGPT, quickSortInPlaceGPT);
} else {
testQuicksort(quickSort, quickSortInPlace);
}
}
main();
===
node q.js
Using geekNews quicksort implementation.
Quicksort: 29.55 ms, In-place: 9.94 ms
node q.js
Using geekNews quicksort implementation.
Quicksort: 28.42 ms, In-place: 9.07 ms
node q.js
Using geekNews quicksort implementation.
Quicksort: 26.91 ms, In-place: 9.15 ms
node q.js --gpt
Using GPT quicksort implementation.
Quicksort: 28.73 ms, In-place: 9.22 ms
node q.js --gpt
Using GPT quicksort implementation.
Quicksort: 26.87 ms, In-place: 9.22 ms
node q.js --gpt
Using GPT quicksort implementation.
Quicksort: 27.97 ms, In-place: 9.30 ms
node --version
v22.14.0
bun q.js
Using geekNews quicksort implementation.
Quicksort: 32.05 ms, In-place: 17.39 ms
bun q.js
Using geekNews quicksort implementation.
Quicksort: 30.97 ms, In-place: 17.82 ms
bun q.js
Using geekNews quicksort implementation.
Quicksort: 29.73 ms, In-place: 16.14 ms
bun q.js --gpt
Using GPT quicksort implementation.
Quicksort: 30.61 ms, In-place: 12.63 ms
bun q.js --gpt
Using GPT quicksort implementation.
Quicksort: 31.09 ms, In-place: 12.76 ms
bun q.js --gpt
Using GPT quicksort implementation.
Quicksort: 33.24 ms, In-place: 12.75 ms
bun --version
1.2.14
deno q.js
Using geekNews quicksort implementation.
Quicksort: 32.30 ms, In-place: 6.79 ms
deno q.js
Using geekNews quicksort implementation.
Quicksort: 26.79 ms, In-place: 6.86 ms
deno q.js
Using geekNews quicksort implementation.
Quicksort: 26.09 ms, In-place: 6.85 ms
deno q.js --gpt
Using GPT quicksort implementation.
Quicksort: 27.18 ms, In-place: 7.92 ms
deno q.js --gpt
Using GPT quicksort implementation.
Quicksort: 25.34 ms, In-place: 8.12 ms
deno q.js --gpt
Using GPT quicksort implementation.
Quicksort: 25.39 ms, In-place: 8.09 ms
deno --version
deno 2.3.3 (stable, release, x86_64-pc-windows-msvc)
v8 13.7.152.6-rusty
typescript 5.8.3
当然、比較は
quickSort()とquickSortInPlace()の2つの関数の性能比較であるべきです........ああ……何を言いたいのか理解しました。何と何を比較すべきかを理解されていなかったのですね……。quick sort アルゴリズムに quicksort と in-place という2つの実装方式があるわけではありません……。
そもそも、Array のマージが組み込まれた上記コード内の
quickSortGPT()、quickSort()(どちらも GPT が出力したコードです)を作成して AI ユーザーに提供している点を問題視していたのです。???? 結果が2倍を超えて3〜4倍も差があるものとして共有しておきながら、2倍まではいかない気がするというのはどういうこと?
今でも40〜50代の開発者と一緒に働こうとすると、何十年も前のやり方で開発しようとする人たちのせいでイライラすることがあるのに、これはやばい。個人的には日本のように若者がバイトや非正規ではなく正規職に就けて、高齢者は日雇いバイト中心で入るほうが、より健全な社会になると思う。韓国は逆ピラミッド型で労働所得を分配しているから、時間がたつほどはしご外しがひどくなる一方だと思う。
このデータはどのように提供されるのでしょうか?
実際に実行してみましたが、やや遅い傾向はあるものの、2倍まではいかないようです。
===
node q.js
Using geekNews quicksort implementation.
Quicksort: 29.55 ms, In-place: 9.94 ms
node q.js
Using geekNews quicksort implementation.
Quicksort: 28.42 ms, In-place: 9.07 ms
node q.js
Using geekNews quicksort implementation.
Quicksort: 26.91 ms, In-place: 9.15 ms
node q.js --gpt
Using GPT quicksort implementation.
Quicksort: 28.73 ms, In-place: 9.22 ms
node q.js --gpt
Using GPT quicksort implementation.
Quicksort: 26.87 ms, In-place: 9.22 ms
node q.js --gpt
Using GPT quicksort implementation.
Quicksort: 27.97 ms, In-place: 9.30 ms
node --version
v22.14.0
bun q.js
Using geekNews quicksort implementation.
Quicksort: 32.05 ms, In-place: 17.39 ms
bun q.js
Using geekNews quicksort implementation.
Quicksort: 30.97 ms, In-place: 17.82 ms
bun q.js
Using geekNews quicksort implementation.
Quicksort: 29.73 ms, In-place: 16.14 ms
bun q.js --gpt
Using GPT quicksort implementation.
Quicksort: 30.61 ms, In-place: 12.63 ms
bun q.js --gpt
Using GPT quicksort implementation.
Quicksort: 31.09 ms, In-place: 12.76 ms
bun q.js --gpt
Using GPT quicksort implementation.
Quicksort: 33.24 ms, In-place: 12.75 ms
bun --version
1.2.14
deno q.js
Using geekNews quicksort implementation.
Quicksort: 32.30 ms, In-place: 6.79 ms
deno q.js
Using geekNews quicksort implementation.
Quicksort: 26.79 ms, In-place: 6.86 ms
deno q.js
Using geekNews quicksort implementation.
Quicksort: 26.09 ms, In-place: 6.85 ms
deno q.js --gpt
Using GPT quicksort implementation.
Quicksort: 27.18 ms, In-place: 7.92 ms
deno q.js --gpt
Using GPT quicksort implementation.
Quicksort: 25.34 ms, In-place: 8.12 ms
deno q.js --gpt
Using GPT quicksort implementation.
Quicksort: 25.39 ms, In-place: 8.09 ms
deno --version
deno 2.3.3 (stable, release, x86_64-pc-windows-msvc)
v8 13.7.152.6-rusty
typescript 5.8.3
....え?
発表前に、Google と Facebook のスポンサー紹介がありました
「牛を失ってから牛小屋を直す」ということわざが、未来への備えをするなという意味ではないのと同じように、
「車輪を再発明するな」も、洞察を得るために時間を投資するなという意味ではないと思います。
こうした言葉がどのような状況で出てきたのか、その前後を切り取ってしまうと本来の意味は歪められます。
アメリカの最大の特産品はドルだと思います
同じ問題を解決するために何度も繰り返しているとコンテキストウィンドウのサイズを超えてしまい、そういうときにAIが壊れるような経験を何度もしたのですが、このような場合は他の方はどうしているのか気になります。 私は何度も試して、どうにも間抜けな振る舞いをし始めたらモデルを変えて、プロンプトウィンドウを新しく開きます。
つい最近の体験ですが、私は最近、自分だけのとても特別な車輪をひとつ作りました。
Nuxtで1000ページ規模のアプリをビルドするのに7分かかっていたのですが、
いくつかの自動化をあきらめて作り直した結果、20秒ビルドに成功したんです。
OSSU オープンソース・ソサエティ・ユニバーシティ - Computer Science 独学する
GeekNewsの初期に紹介されていましたね。その間にかなり多くの内容が追加されました。
ご回答ありがとうございます!
添付していただいた2つ目の
quickSortInPlace()、これも遅い実装ですね。以下のコードを実行してみてください。
function quickSortInPlace(arr, left = 0, right = arr.length - 1) {
if (left >= right) return;
const pivotIndex = partition(arr, left, right);
quickSortInPlace(arr, left, pivotIndex - 1);
quickSortInPlace(arr, pivotIndex + 1, right);
}
function partition(arr, left, right) {
const pivot = arr[right];
let i = left;
for (let j = left; j < right; j++) {
if (arr[j] < pivot) {
[arr[i], arr[j]] = [arr[j], arr[i]];
i++;
}
}
[arr[i], arr[right]] = [arr[right], arr[i]];
return i;
}
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[arr.length - 1];
const left = [];
const right = [];
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
const repeat = 100;
const arrLength = 10000;
const unsortedArray = new Array<number>();
for(let i = 0; i < arrLength; i++)
unsortedArray.push(Math.round(Math.random() * arrLength));
let sorted: Array<number>;
const qb = performance.now();
for(let i = 0; i < repeat; i++)
sorted = quickSort(unsortedArray);
const qe = performance.now();
const rqb = performance.now();
for(let i = 0; i < repeat; i++) {
let copied = [...unsortedArray];
quickSortInPlace(copied);
}
const rqe = performance.now();
console.log(
q: ${qe - qb} ::: rq: ${rqe - rqb});深い洞察を感じる文章です。さすが a16z です。
普段はあまりコメントしないのですが、この文章にだけコメントを残した理由は、著者の考えにかなり共感しているからです。重要なのはAIやLLMそのものではなく、どんな環境が来ても開発者としての「自分」が備えていなければならない、ということだと思います。
LLMは学習されたソースの特性上、世界中に広がるオンラインデータの平均値に近いデータを主に提供します。(前述のjsクイックソートがその証拠です。)だから私はたいてい、思想やデザインの面で一般的な観点とどの程度一致しているか、ずれているか、あるいはこれまでどこに聞けばいいのか微妙だった内容を質問するために多く使っています。
さらにこれ以上議論を続けることに、どんな意味があるのかよく分かりません。
そもそも、AIが生成したコードにはある程度のリスク要因があり得るので、十分に精査したうえで適切に活用するのがよい、という意見なのであれば、著者の文章のどの考え方が偏っているのかを説明してもらえればよいのではないでしょうか。要約にも「文脈のない scaffold/草案コードを素早く提供することはできるが、完全な設計とチューニングは人間の開発者の役割である」とあり、似た意味の内容が含まれていますから。
基本的に、コレクションの生成・運用・マージに伴う負担への理解がまったく感じられないコードだと言えます。C++ の場合、すでに約10年前に Move Constructor に関する提案・実装も登場しており、メモリコピーに関するコストには常に鋭敏であることが基本中の基本です。quick sort はその仕組み上、すべての値の index を確定できるアルゴリズムであり、各フィールドはランダムアクセスできるようにしておくのが望ましいです。
マニアックな最適化なしでも、上記の内容だけを適用すれば、リンク先で示されている方式とは2倍以上の性能差が出ます。
return [...quickSort(left), ...equal, ...quickSort(right)];
この部分のコードをよく見て、考えてみてください。