Local Vibe コーディング: LM Studio、VS Code、Continue

コード (いわゆる Vibe コーディング) の作成にニューラル ネットワークを使用したいと考えていて、たとえば Nvidia RTX ビデオ カードを備えたかなり強力なコンピューターを持っている場合は、環境全体を完全に無料でマシンに展開できます。これにより、有料サブスクリプションの問題が解決され、コードがどこにも送信されないため、NDA の下でプロジェクトを安全に操作できるようになります。この投稿では、LM Studio、VS Code、Continue 拡張機能のローカル バンドルをアセンブルする方法について説明します。

ローカル Vibe コーディング用ツール

快適な作業のためには、次の 3 つの主要なコンポーネントが必要です。
LM ​​Studio: ローカル LLM をダウンロードして実行するための便利なアプリケーションです。 GGUF モデルの操作に伴うすべての複雑さを引き受け、OpenAI API と互換性のあるローカル サーバーを構築します。
VS Code: 人気があり、馴染みのあるコード エディタです。
Continue: ニューラル ネットワークを作業環境に直接統合する VS Code の拡張機能。チャットしたり、リファクタリング用のコードをハイライトしたり、オートコンプリートをサポートしたりできます。

ハードウェア要件

ローカル言語モデルはメモリを大量に消費します。
ビデオ カード (GPU): 8 GB VRAM 以上を搭載した Nvidia (70 ~ 80 億のパラメータを持つモデルで快適に作業するため)。重いモデルには 16 GB の VRAM が必要です。
ディスク容量: さまざまなダウンロード モデルを保存するために約 500 GB。

リンクの構成

セットアップ プロセスは非常に簡単で、ターミナルでの複雑な操作は必要ありません。
1. LM Studioをダウンロードしてインストールします。組み込みの検索を使用して、Qwen Codergemma3:12b などの軽量モデルを見つけます。
2. LM Studio で、「ローカルサーバー」タブに移動し、「サーバーの開始」をクリックします。デフォルトでは、`http://localhost:1234/v1` で起動します。
3. VS Code を開き、プラグイン ストアからContinue 拡張機能をインストールします。
4. Continue 構成ファイルを開き、LM Studio から「openai」プロバイダーとローカルサーバーのアドレスを指定して新しいモデルを追加します。

その後、「続行」サイドバーでローカル LLM と直接通信し、コードについて質問したり、新しいコンポーネントを生成したりできます。

なぜこれが機能するのでしょうか?

前に書いたように、LLM はフラット構造と WET (Write Everything Twice) コードの方が優れています。ローカル パラメーター モデルは、複雑なアーキテクチャの設計に関しては GPT-4 のような巨大モデルに劣るかもしれませんが、ボイラープレート コードの生成、単純な関数のリファクタリング、およびラピッド プロトタイピングについては十分以上の能力を備えています。

さらに、ローカル Vibe コーディングを使用すると、コードがマシンから離れることはありません。このため、この組み合わせは企業開発や機密データの取り扱いに最適です。

出力

ローカル ニューラル ネットワークは、プログラマーを完全に置き換えたり、複雑なシステムを設計したりすることはできません。ただし、LM Studio + VS Code + Continue の組み合わせにより、クラウド サービスからの独立性が提供され、プライバシーが維持されます。これは、小さなモデルの制限を我慢し、プロジェクト アーキテクチャを独立して制御する意欲がある場合、日常的なタスクに完全に機能する補助ツールです。

リンク

https://code.visualstudio.com/
https://lmstudio.ai/
https://continue.dev/

ソース

https://youtu.be/IqqCwhG46jY
https://www.youtube.com/watch?v=7AImkA96mE8

Docker で macOS を実行する

不可能だという人々の反対にもかかわらず、Docker で macOS を実行することは可能であり、おそらく macOS にはこれに抵抗できるある種の保護システムがあると考えられます。

PC マシンで macOS を実行する古典的な方法には、これまで次のようなものがありました。
*ハッキントッシュ
* VMWare を使用した仮想化など

Hackintosh は、オリジナルの Mac と同様または非常に近いハードウェアの存在を前提としています。仮想化ではハードウェアに特定の要件が課されますが、一般的には Hackintosh の場合ほど厳格ではありません。ただし、仮想化の場合、macOS は仮想環境での作業用に最適化されていないため、パフォーマンスの問題が発生します。

最近ではDocker上でmacOSを実行できるようになりました。これは、Docker 上で実行する既製の macOS イメージを提供する Docker-OSX プロジェクトによって可能になります。 Docker-OSX は Apple の公式プロジェクトではなく、サポートされていないことに注意してください。ただし、Docker 上で macOS を実行し、それを使用してアプリケーションの開発とテストを行うことができます。

Docker で macOS を実行する最初のプロジェクトの 1 つ:
https://github.com/sickcodes/Docker-OSX

ただし、完全に起動することはできませんでした。 Recovery OS にロードした後、キーボードとマウスが外れてしまい、インストールを続行できませんでした。同時に、最初のブート メニューではキーボードが機能します。おそらく実際には、このプロジェクトはあまり積極的にサポートされておらず、Windows 11 + WSL2 + Ubuntu で実行すると特定の問題がいくつか発生します。

現在最も活発なプロジェクトの 1 つ:
https://github.com/dockur/macos

Docker で macOS を実行できるようにします。インターフェイスは VNC(?) 転送を介してブラウザ経由で動作します。起動後、macOS は http://localhost:5900 で利用可能になります。

このプロジェクトを実行し、Windows 11 + WSL2 + Ubuntu に macOS Big Sur (2020 分) をインストールすることができましたが、構成ファイルを変更するだけで済みました。

environment:
    VERSION: "11"
    RAM_SIZE: "8G"
    CPU_CORES: "4"

バージョン: 「11」は macOS のバージョンです。この場合は Big Sur
RAM_SIZE: 「8G」は、macOS に割り当てられた RAM の量です。
CPU_CORES: 「4」は macOS に割り当てられた CPU コアの数です。

現時点では、macOS tahoe (16) を実行することも可能ですが、プロジェクト開発者が勇敢に解決しようとしている問題が数多くあります。

macOS を起動するこの独自の方法を使用すると、Mac 以外のハードウェアで macOS を試し、十分に苦労した後、Mac を購入することができます。ただし、古いシステムでのソフトウェアのテストや一般的な開発には役立ちます。

WSL2 で Swift を構築する (Linux)

Swift エコシステムは Apple プラットフォームの外で積極的に開発されており、現在では Windows Subsystem for Linux (WSL2) を使用して Windows 上で Swift エコシステムに書き込むのが非常に快適です。 Linux/WSL でのアセンブリの場合、独自の Apple フレームワーク (SwiftUI、UIKit、AppKit、CoreData、CoreML、ARKit、SpriteKit、その他の iOS/macOS 固有のライブラリなど) なしで、Swift の軽量バージョンが利用可能であることを考慮する価値がありますが、コンソール ユーティリティとバックエンドの場合はこれで十分です。この投稿では、環境を準備し、WSL2 内のソース コードから Swift コンパイラーを構築するプロセスを段階的に説明します (例として Ubuntu/Debian を使用)。

パッケージのリストとシステム自体を更新します。

sudo apt update && sudo apt upgrade -y

ビルドに必要な依存関係をインストールします。

sudo apt install -y \
  git cmake ninja-build clang python3 python3-pip \
  libicu-dev libxml2-dev libcurl4-openssl-dev \
  libedit-dev libsqlite3-dev swig libncurses5-dev \
  pkg-config tzdata rsync

コンパイラとリンカー (LLVM と LLD) をインストールします。

sudo apt install -y llvm lld

すべての依存関係を含む Swift リポジトリのクローンを作成します。

git clone https://github.com/apple/swift.git
cd swift
utils/update-checkout --clone

`swiftly` と既製の swift を swiftc でインストールする

curl -O https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz && \
tar zxf swiftly-$(uname -m).tar.gz && \
./swiftly init --quiet-shell-followup && \
. "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh" && \
hash -r

ビルドを開始しましょう (これには長い時間がかかります)。

utils/build-script \
  --release-debuginfo \
  --swift-darwin-supported-archs="x86_64" \
  --llvm-targets-to-build="X86" \
  --skip-build-benchmarks \
  --skip-test-cmark \
  --skip-test-swift \
  --skip-ios \
  --skip-tvos \
  --skip-watchos \
  --skip-build-libdispatch=false \
  --skip-build-cmark=false \
  --skip-build-foundation \
  --skip-build-lldb \
  --skip-build-xctest \
  --skip-test-swift

ビルドが完了したら、コンパイラーへのパスを PATH に追加します (ビルド フォルダーへのパスを指定します)。

export PATH=/root/Sources/3rdparty/build/Ninja-RelWithDebInfoAssert/swift-linux-x86_64/bin/swiftc:$PATH

インストールされている Swift のバージョンが動作していることを確認します。

swift --version

テスト ファイルを作成して実行します。

echo "print(\"Hello, World!\")" > hello.swift
swift hello.swift

バイナリをコンパイルして実行することもできます。

swiftc hello.swift
./hello

ソース

パターンインタープリターの実践

前回の記事では、インタープリター パターンの理論を検討し、AST ツリーとは何か、終端式と非終端式を抽象化する方法を学びました。今回は、理論から離れて、このパターンが私たち全員が毎日使用する本格的な商業プロジェクトにどのように適用されるかを見てみましょう!

ネタバレ: ブラウザでこのテキストを読んでいるだけで、あなたは今 Interpreter パターンを使用している可能性があります。

業界におけるこのパターンの使用例の中で最も印象的で、おそらく最も重要なものの 1 つは JavaScript です。もともと「膝の上で」作成されたこの言語は、今日ではまさに解釈という概念のおかげで何十億ものデバイスで動作します。

インターネットを変えた 10 日間

JavaScript の歴史には伝説がたくさんあります。 1995 年、ネットスケープ コミュニケーションズで働いていたブレンダン アイヒは、ブラウザ (Netscape Navigator) で直接実行して Web ページをインタラクティブにする簡単なスクリプト言語を作成するという任務を与えられました。経営陣は、当時非常に人気があった Java に似た構文を持つものを望んでいましたが、プロのエンジニアではなく、Web デザイナーを対象としていました。

Eich がこの言語の最初のプロトタイプを書くのにわずか 10 日しかありませんでした。この言語は当時 Mocha と呼ばれていました (その後 LiveScript になり、マーケティング上の理由から JavaScript のみになりました)。このラッシュは偶然ではありませんでした。Microsoft はそれに熱心に追随しており、同時に Internet Explorer ブラウザに埋め込むための独自のスクリプト言語VBScript を積極的に準備していました。 Netscape は、迫り来るブラウザ戦争に負けないよう、早急に対応策を発表する必要がありました。

複雑なコンパイラをマシンコードに記述する時間がありませんでした。アイヒにとっての明白かつ最速のソリューションは、古典的なインタプリタのアーキテクチャでした。

最初のインタープリター (SpiderMonkey) は次のように機能しました:

<オル>

  • ページからスクリプトのテキスト ソース コードを読み取ります。
  • 字句アナライザーはテキストをトークンに分割しました。
  • パーサーは抽象構文ツリー (AST) を構築しました。インタプリタ パターンの観点から見ると、このツリーは終端式 (文字列、42 などの数値) と非終端式 (関数呼び出し、If や While などのステートメント) で構成されていました。
  • その後、仮想マシンはこのツリーを段階的に「走査」し、ツリーに埋め込まれた命令を各ノードで実行します (Interpret() と同様のメソッドを呼び出します)。
  • コンテキストとオブジェクト

    従来の実装では Interpret(Context context) メソッドに渡す必要があった Context オブジェクトを覚えていますか?インタプリタは現在のメモリ状態を保存するためにこれを必要とします。

    JavaScript の場合、トップレベルでのこのコンテキストの役割はグローバル オブジェクト (ブラウザのウィンドウなど) によって実行されます。 AST ノードが、たとえば document.write(“Hello”) 経由で画面にテキストを書き込もうとすると、インタプリタはそのコンテキスト (ドキュメント オブジェクト) にアクセスし、目的の内部ブラウ​​ザ API を呼び出します。

    JavaScript が DOM (ドキュメント オブジェクト モデル) と簡単にやり取りできるのは、インタープリタのおかげです。これらはすべて、ツリー ノードによってアクセスされるコンテキスト内の単なるオブジェクトです。

    インタプリタの進化: JIT コンパイル

    歴史的に、ブラウザの JS は長い間「純粋な」インタープリタであり続けてきました。そして、これには速度が遅いという大きな欠点がありました。スクリプトが実行されるたびにツリーを解析し、各ノードをゆっくりと走査すると、複雑な Web アプリケーションの速度が低下しました。

    2008 年の Google の V8 エンジン (Chrome に組み込まれている) の登場により、革命が起こりました。エンジニアは、現代の Web には 1 人のインタプリタでは不十分であることに気づきました。エンジンはより複雑になりました。引き続き AST ツリーを構築しますが、JIT (ジャストインタイム) コンパイルを使用するようになりました。

    最新の JS エンジン (V8、SpiderMonkey) は、複雑なパイプラインのように動作します。

    <オル>

  • 高速かつダムなベース インタープリターは、コンパイルを待つことなく、即座に JS コードの実行を開始します (ここでも古典的なパターンが機能します)。
  • 並行して、エンジンはコードの「ホット」セクション(何千回も呼び出されるループまたは関数)を監視します。
  • これらのセクションは、遅いインタープリタをバイパスし、JIT コンパイラによって最適化されたマシンコードに直接コンパイルされます。
  • インタプリタの即時起動とコンパイルの計算能力の組み合わせにより、JavaScript は世界を席巻し、サーバー (Node.js) やモバイル アプリケーション (React Native) の言語になりました。

    ゲーム業界の通訳

    ヘビー コンピューティングでは C++ が優勢ですが、インタプリタ パターンはゲーム開発におけるゲーム ロジック作成の業界標準です。何のために?これにより、ゲーム デザイナーはエンジンを「ドロップ」するリスクやエンジンを常に再コンパイルする必要がなく、ゲームを作成できます。

    歴史的な優れた例は、UnrealScript です。これは、Unreal Championship および Gears of War ゲームのロジックが Unreal Engine 1、2、3 で記述された言語です。テキストはコンパクトな抽象マシン バイトコードにコンパイルされ、エンジンの仮想マシンによって段階的に (解釈) されます。

    ビジュアル グラフ スクリプト (ブループリント)

    現在、テキストはビジュアル プログラミング、つまり Unreal Engine 4 および 5 のブループリント システムに置き換えられています。

    Unreal Engine でブループリントを開いたことがあれば、多くのノードがワイヤで接続されているのを見たことがあるでしょう。アーキテクチャ的には、ブループリント グラフ全体が画面上に描画される巨大な抽象構文ツリー (AST) です

    <オル>

  • ターミナル式: 定数ノード。たとえば、単純に数字 42 または文字列を格納するノードです。解釈されると特定の値を返します。
  • 非端末式: 計算ノード (追加) またはフロー制御ノード (分岐)。これらには引数入力があり、インタープリタは結果を出力ピンとして生成する前に、最初に再帰的に評価します。
  • ここでのコンテキストの役割は、特定のゲーム オブジェクト (アクター) のインスタンスのメモリによって果たされます。インタプリタ マシンはこのグラフ内を安全に「ウォーク」し、データをリクエストして遷移を実行します。

    インタープリタは他にどこで使用されますか?

    インタプリタ パターンは、動的な命令を実行する必要があるほとんどすべての複雑なシステムに見られます。以下に商用ソフトウェアの例をいくつか示します。

    • インタープリタ型プログラミング言語 (Python、Ruby、PHP)。 ランタイム全体は古典的なパターンに基づいています。たとえば、CPython リファレンス実装では、まず .py スクリプトを AST に解析し、バイトコードにコンパイルしてから、巨大な仮想マシン (コンピューティング ループ) がそのバイトコードを段階的に解釈します。
    • Java 仮想マシン (JVM)。 最初、Java コードは機械語命令ではなくバイトコードにコンパイルされます。アプリケーションを実行すると、JVM はインタープリターとして機能します (ただし、V8 と同様に積極的な JIT コンパイルが行われます)。
    • データベースと SQL PostgreSQL または MySQL で SQL クエリ (SELECT * FROM ユーザー) を発行すると、データベース エンジンがインタープリタとして機能します。字句解析を実行し、AST クエリ ツリーを構築し、実行プランを生成して、テーブルの行を反復処理することでこのプランを文字通り「解釈」します。
    • 正規表現 (RegEx)。 正規表現エンジンは内部で文字列パターン (^\d{3}-\d{2}$ など) を解析して状態グラフ (NFA/DFA オートマトン) にし、内部インタープリタがそれを通過させて、各入力文字をこのグラフの頂点と照合します。
    • Unity シェーダー グラフ / アンリアル マテリアル エディタ – ビジュアル ノードをモジュラー シェーダー コード (GLSL/HLSL) に解釈します。
    • Blender ジオメトリ ノード – 数学的および幾何学的演算を解釈して、リアルタイムで手続き的に 3D モデルを生成します。

    合計

    Interpreter パターンは、「独自の計算機を作成する」という範囲をはるかに超えています。これは最も強力な業界標準です。毎日ブラウザの舞台裏でギガバイトのコードを実行する JavaScript エンジンから、C++ の知識がなくても複雑なロジックを構築できるゲーム デザイナーに至るまで、インタプリタは依然として現代の IT 開発において最も重要なアーキテクチャ概念の 1 つです。

    ホルマリンなしの実際の図をブロックします

    ブロック図は、複雑なアルゴリズムを理解しやすく構造化されたアクションシーケンスに変えるのに役立つ視覚ツールです。プログラミングからビジネスプロセス管理まで、最も複雑なシステムの視覚化、分析、最適化のための普遍的な言語として機能します。

    道路の代わりにロジックであり、都市の代わりにアクションがある地図を想像してください。これはブロック図であり、最も混乱するプロセスでナビゲーションのための不可欠なツールです。

    例1:簡素化されたゲームの起動スキーム
    仕事の原則を理解するために、簡単なゲームの起動スキームを提示しましょう。

    このスキームは、すべてが失敗することなく起こるときの完璧なスクリプトを示しています。しかし、実際の生活では、すべてがはるかに複雑です。

    例2:データ読み込みでゲームを開始するための拡張スキーム
    最新のゲームでは、ユーザーデータ、保存、または設定をダウンロードするためにインターネット接続が必要になることがよくあります。これらの手順をスキームに追加しましょう。

    このスキームはすでにより現実的ですが、何かがうまくいかない場合はどうなりますか?

    どうでしたか:インターネットの損失で「壊れた」ゲーム

    プロジェクトの開始時に、開発者は考えられるすべてのシナリオを考慮することができませんでした。たとえば、彼らはゲームの主な論理に焦点を合わせており、プレーヤーにインターネット接続がある場合に何が起こるか考えませんでした。

    このような状況では、彼らのコードのブロック図は次のようになります。

    この場合、エラーを発行したり正しく閉じたりする代わりに、接続がないために受け取っていないデータを待つ段階でゲームが凍結しました。これにより、「ブラックスクリーン」が発生し、アプリケーションが凍結されました。

    それがどのようになったか:ユーザーの苦情の修正

    ホバリングに関する多くのユーザーの苦情の後、開発者チームは、エラーを修正する必要があることに気付きました。彼らは、アプリケーションが接続の欠如に正しく応答できるエラー処理ユニットを追加することにより、コードを変更しました。

    これは、修正されたブロック図がどのように見えるかであり、両方のシナリオが考慮されます。

    このアプローチのおかげで、ゲームはユーザーに問題について正しく通知し、場合によってはオフラインモードに移動してゲームを続けることができます。これは、ブロック図が非常に重要である理由の良い例です:開発者に、実行の理想的な方法だけでなく、すべての可能な障害についても考えさせ、最終製品をより安定して信頼できるものにします。

    不確実な行動

    ハンギングとエラーは、プログラムの予測不可能な動作の例の1つにすぎません。プログラミングでは、不確実な動作(未定義の行動)の概念があります – これは、言語の基準が特定のケースでプログラムの動作を説明しない状況です。

    これは何にもつながる可能性があります:撤退のランダムな「ごみ」からプログラムの失敗や深刻なセキュリティの脆弱性まで。たとえば、メモリを使用して作業するとき、無期限の動作はしばしば発生します。たとえば、Cの言語の線で

    言語cの例:

    開発者がラインをバッファーにコピーしたが、ゼロシンボル( `\ 0`)を最後に追加するのを忘れたと想像してください。

    これがコードがどのように見えるかです:

    #include 
    
    int main() {
    char buffer[5];
    char* my_string = "hello";
    
    memcpy(buffer, my_string, 5);
    
    printf("%s\n", buffer);
    return 0;
    }
    

    予想される結果:「こんにちは」
    実際の結果は予測不可能です。

    なぜこれが起こっているのですか? spitifier`%sを使用した「printf」関数は、線がゼロシンボルで終了することを期待しています。彼がそうでない場合、彼女はハイライトされたバッファーの外でメモリを読み続けます。

    次に、このプロセスのブロック図を紹介します。

    これは、ブロック図が非常に重要である理由の明確な例です。開発者に、理想的な実行方法だけでなく、そのような低レベルの問題を含むすべての可能な障害についても考えさせ、最終製品のより安定性と信頼性を高めます。

    Pixel Perfect:宣言性の時代の神話または現実?

    インターフェイス開発の世界では、共通の概念があります – 「ロッジに完璧なピクセル」。これは、デザインマシンの最小ピクセルへの最も正確な複製を意味します。長い間、それは、特に古典的なウェブデザインの時代において、ゴールドスタンダードでした。しかし、宣言的なマイルの到着とさまざまなデバイスの急速な成長により、「Pixel Perfect」の原則はより一時的になりつつあります。理由を把握してみましょう。

    Imperial Wysiwyg vs.宣言コード:違いは何ですか?

    従来、多くのインターフェイス、特にデスクトップは、編集者の命令的なアプローチまたはwysiwyg(あなたが見るものです)を使用して作成されていました。このようなツールでは、デザイナーまたは開発者は要素を直接操作し、ピクセルに正確にキャンバスに配置します。それはグラフィックエディターとの作業に似ています – あなたはあなたの要素がどのように見えるかを見ることができ、あなたは間違いなくそれを配置することができます。この場合、「Pixel Perfect」の達成は非常に現実的な目標でした。

    ただし、現代の開発は、宣言マイルにますます基づいています。これは、コンピューターに「このボタンをここに置く」ように指示するのではなく、取得したいものを説明することを意味します。たとえば、要素の特定の座標を示す代わりに、そのプロパティについて説明します。「このボタンは赤く、すべての側面から16pxのインデントを持ち、容器の中心にある必要があります。」 Freimvorkiは、React、Vue、Swiftui、Jetpackのようなものです。この原則を使用するだけです。

    なぜ「Pixel Perfect」が多くのデバイスで宣言的なマイルで動作しない

    iPhone 15 Pro Max、Samsung Galaxy Fold、iPad Pro、および4K解像度で等しく見栄えの良いアプリケーションを作成すると想像してください。これらの各デバイスには、画面解像度、ピクセル密度、パーティ、および物理サイズが異なります。

    宣言的アプローチを使用する場合、システム自体は、すべてのパラメーターを考慮して、特定のデバイスに説明されたインターフェイスを表示する方法を決定します。厳しい座標ではなく、ルールと依存関係を設定します。

    * 適応性と応答性:宣言マイルの主な目標は、適応的で応答性のあるインターフェイスを作成することです。これは、読みやすさを壊して維持せずに、インターフェイスが画面のサイズと方向に自動的に適応する必要があることを意味します。各デバイスで「Pixel Perfect」を試みた場合、同じインターフェイスの無数のオプションを作成する必要があります。
    * ピクセル密度(DPI/PPI):デバイスのピクセル密度は異なります。スケーリングを考慮しない場合、高密度のデバイス上の100の「仮想」ピクセルのピクセルの同じ要素は、低密度デバイスよりもはるかに小さく見えます。宣言的なフレームワークは、物理的なピクセルによって抽象化され、論理ユニットを使用します。
    * 動的コンテンツ:最新のアプリケーションのコンテンツは、しばしば動的です – その体積と構造は異なる場合があります。ピクセルに激しくぼろぼろにした場合、テキストや画像の変更はレイアウトの「崩壊」につながります。
    * さまざまなプラットフォーム:さまざまなデバイスに加えて、異なるオペレーティングシステム(iOS、Android、Web、デスクトップ)があります。各プラットフォームには、独自の設計、標準コントロール、フォントがあります。すべてのプラットフォームで完全に同一のピクセル完璧なインターフェイスを作成しようとすると、不自然なタイプとユーザーエクスペリエンスの低下につながります。

    古いアプローチは消えませんでしたが、進化しました

    インターフェイスへのアプローチは、「必須」と「宣言」の間のバイナリ選択ではないことを理解することが重要です。歴史的に、各プラットフォームには、インターフェイスの作成に対する独自のツールとアプローチがありました。

    * ネイティブインターフェイスファイル: iOSの場合は、Android-XMLマーキングファイルのXIB/ストーリーボードでした。これらのファイルは、ピクセルに最適なWysiWygレイアウトであり、エディターと同様に無線に表示されます。このアプローチはどこにも消えておらず、開発を続けており、最新の宣言的なフレームと統合されています。たとえば、AppleとJetpackのSwiftuiはAndroidで構成され、純粋に宣言的なコードのパスで出発しましたが、同時に古典的なレイアウトを使用する機会を保持しました。
    * ハイブリッドソリューション:多くの場合、実際のプロジェクトでは、アプローチの組み合わせが使用されます。たとえば、アプリケーションの基本構造は宣言的に実装できます。特定のために、要素の正確な位置決めを必要とするため、プラットフォームの詳細を考慮して、より低レベルの命令的な方法を使用するか、ネイティブコンポーネントを開発できます。

    モノリスから適応性への:デバイスの進化が宣言的なマイルを形成する方法

    デジタルインターフェイスの世界は、過去数十年にわたって大きな変化を遂げてきました。固定許可を持つ固定コンピューターから、さまざまなユーザーデバイスの指数関数的な成長の時代になりました。今日、当社のアプリケーションは次のことで等しくうまく機能するはずです

    * すべてのフォームファクターと画面サイズのスマートフォン
    * タブレット独自の方向モードと分離画面があります。
    * ラップトップとデスクトップさまざまな許可証のモニター。
    * テレビとメディアセンター、リモートで制御されています。テレビでさえ、最小限のボタンを備えたApple TVリモート
    のように単純なものになる可能性があることは注目に値します。インターフェイスは、特定のリモートコントロールと対話する「方法」を追加することなく、「まるでそれ自体のように」機能するはずです。
    * ミニマルな画面を備えたスマートウォッチとウェアラブルデバイス
    * 仮想現実ヘルメット(VR)。空間インターフェイスに対するまったく新しいアプローチが必要です。
    * 拡張現実デバイス(AR)、現実世界に関する情報を適用します。
    * 自動車情報およびエンターテイメントシステム
    *そして、家電製品:感覚スクリーンを備えた冷蔵庫と、スマートオーブンとスマートハウスのシステムへのインタラクティブなディスプレイを備えた洗濯機から。

    これらの各デバイスには、物理的寸法、パーティー比、ピクセル密度、入力方法(タッチスクリーン、マウス、コントローラー、ジェスチャー、ボーカルコマンド)、および重要なことに、ユーザー環境の微妙さ。たとえば、VR Shleshには深い浸漬が必要であり、外出先でのスマートフォンの高速で直感的な作業が必要ですが、冷蔵庫のインターフェイスは簡単で大きくて、迅速なナビゲーションを使用する必要があります。

    古典的なアプローチ:個々のインターフェイスをサポートする負担

    デスクトップの優位性と最初のモバイルデバイスの時代において、通常のビジネスは、個々のインターフェイスファイルのの作成とサポートでした。

    * iOS に基づく開発は、xcodeでストーリーボードまたはxibファイルを使用する必要があることが多く、Objective-cまたはswiftのコードを作成しました。
    * android XMLマーキングファイルとJavaまたはKotlinのコードが作成されました。
    * WebインターフェイスはHTML/CSS/JavaScriptをオンにしました。
    * c ++アプリケーションの場合さまざまなデスクトッププラットフォームで、特定のフレームワークとツールが使用されました。
    * Windows では、これらはMFC(Microsoft Foundationクラス)、手動描画要素を備えたWin32 API、またはダイアログウィンドウとコントロール要素のリソースファイルを使用していました。
    *グラフィックインターフェイスを直接制御するためのココア(Objective-C/Swift)または古いカーボンAPIが macos で使用されました。
    * linux/unixのようなシステムでは、GTK+やQTなどのライブラリがよく使用され、XMLのようなマーキングファイル(QTデザイナーの.UIファイルなど)または要素の直接ソフトウェアの作成を介して、インターフェイスを作成するためのウィジェットとメカニズムのセットを提供しました。

    このアプローチにより、各プラットフォームを最大限に制御できるようになり、特定のすべての機能とネイティブ要素を考慮することができます。しかし、彼には大きな欠点がありました。努力の複製とサポートのための途方もないコスト。設計または機能のわずかな変更には、実際には独立したコードベースがいくつかの権利を導入する必要がありました。これは、開発者チームにとって本当の悪夢になり、新しい機能の出力を遅くし、エラーの可能性を高めました。

    宣言マイル:多様性のための単一言語

    宣言マイルが支配的なパラダイムとして登場したのは、この急速な複雑さに対応していました。 React、Vue、Swiftui、Jetpackのようなframwsはを構成します。

    宣言的アプローチの主なアイデア:システムがすべての要素を描画する方法(命令)を「どのように」(命令したいか(宣言)」と説明する代わりに。インターフェイスのプロパティと条件を設定し、フレームワークは特定のデバイスに最適に表示する方法を決定します。

    これは、次の重要な利点のおかげで可能になりました。

    1。プラットフォームの詳細からの抽象化:宣言的なfraimvorkiは、各プラットフォームの低レベルの詳細を忘れるように特別に設計されています。開発者は、単一の転送されたコードを使用して、より高いレベルの抽象化でコンポーネントとその関係を説明します。
    2。自動適応と応答性: freimvorki 自動スケーリング、要素のレイアウト、およびさまざまなサイズの画面、ピクセル密度、入力方法へのレイアウトと適応の変更について責任を負います。これは、FlexBoxやグリッドなどの柔軟なレイアウトシステムの使用、および「論理ピクセル」や「DP」に似た概念を使用して達成されます。
    3。ユーザーエクスペリエンスの一貫性:外部の違いにもかかわらず、宣言的アプローチにより、デバイスのファミリー全体で行動と相互作用の単一のロジックを維持できます。これにより、テストプロセスが簡素化され、より予測可能なユーザーエクスペリエンスが提供されます。
    4。開発とコスト削減の加速:多くのプラットフォームで作業できるのと同じコードを使用すると、開発とサポートの時間とコストによってが大幅に削減されます。チームは、同じインターフェイスを繰り返し書き直すことではなく、機能と設計に焦点を合わせることができます。
    5。 Freimvorkiは新しいテクノロジーをサポートするために更新でき、すでに書かれたコードがこのサポートを受け取ることは比較的シームレスです。

    結論

    宣言的なマイルは、単なるファッションのトレンドではなく、モノのインターネット(IoT)やスマート世帯家電などのユーザーデバイスの急速な開発によって引き起こされる必要な進化ステップです。これにより、開発者と設計者は、各プラットフォームの無限の特定の実装にownれなくなることなく、複雑で適応的で均一なインターフェイスを作成できます。各ピクセルの命令制御から目的の状態の宣言的記述への遷移は、将来のインターフェイスの世界では、表示される画面に関係なく、柔軟で、転送され、直感的であるであるべきであるという認識です。

    プログラマー、デザイナー、ユーザーは、この新しい世界で生きる方法を学ぶ必要があります。 特定のデバイスまたは解像度に合わせて設計されたPixel Perfectの追加の詳細は、開発とサポートのために不必要な時間コストにつながります。さらに、このような過酷なレイアウトは、限られた入力テレビ、VR、ARシフトなどの標準以外のインターフェイスを備えたデバイスや、将来の他のデバイスでも機能しない場合がありますが、今日でもわかりません。柔軟性と適応性 – これらは、現代世界で成功したインターフェイスを作成するための鍵です。

    バイブコアトリック:なぜLLMが固体、乾燥、きれいに動作しないのか

    CHATGPTなどの大規模な言語モデル(LLM)の開発により、ますます多くの開発者がそれらを使用してコードを生成し、設計アーキテクチャを生成し、統合を加速します。ただし、実用的なアプリケーションでは、それは顕著になります。建築の古典原則 – 固体、乾燥、清潔 – LLMコドゲンデーションの特性とうまくやっていません。

    これは、原則が時代遅れであることを意味するものではありません – それどころか、それらは手動開発と完全に連携しています。しかし、LLMでは、アプローチを適応させる必要があります。

    なぜLLMが建築原則に対処できないのか

    カプセル化

    インクシレーションでは、システムの一部間の境界、開発者の意図に関する知識を理解し、厳格なアクセス制限に従う必要があります。 LLMは、多くの場合、構造を簡素化したり、理由もなくフィールドを公開したり、実装を複製したりします。これにより、コードはエラーに対してより脆弱になり、アーキテクチャの境界に違反します。

    要約とインターフェイス

    抽象的な工場や戦略などの設計パターンには、システムの全体的な見方が必要であり、そのダイナミクスを理解する必要があります。モデルは、実装を保証せずに明確な目的なしでインターフェイスを作成したり、レイヤー間の接続に違反したりできます。結果は、過剰または非機能的アーキテクチャです。

    dry(donolt繰り返し自分で繰り返します)

    LLMは、繰り返しコードを最小限に抑えようとはしません – それどころか、一般的なロジックを作成するよりもブロックを複製する方が簡単です。リクエストに応じてリファクタリングを提供できますが、デフォルトでは、たとえ冗長性につながる場合でも、「自己適切な」フラグメントを生成する傾向があります。

    クリーンアーキテクチャ

    Cleanは、厳格な階層、フレームワークからの独立性、指向性の依存性、およびレイヤー間の最小のつながりを意味します。このような構造の生成には、システムのグローバルな理解が必要です。また、LLMは、建築の完全性ではなく、単語の確率のレベルで機能します。したがって、コードは混合され、依存の方向に違反し、レベルへの単純化された分割が違反されます。

    LLMを使用するときにうまく機能する

    乾燥する代わりに濡れています
    WET(すべてを2回書く)アプローチは、LLMでの作業においてより実用的です。コードの複製は、保持のモデルからのコンテキストを必要としません。つまり、結果は予測可能であり、正しく修正しやすくなります。また、非自明なつながりやバグの可能性を減らします。

    さらに、複製はモデルの短いメモリを補うのに役立ちます。いくつかの場所でロジックの特定の断片が見つかった場合、LLMはそれをさらなる生成で考慮に入れる可能性が高くなります。これにより、伴奏が簡素化され、「忘却」に対する抵抗が増加します。

    カプセル化の代わりに単純な構造

    複雑なカプセル化を回避し、コードの部分間のデータの直接送信に依存すると、生成とデバッグの両方を大幅に簡素化できます。これは、MVPの迅速な開発または作成で特に当てはまります。

    簡略化されたアーキテクチャ

    プロジェクトのシンプルでフラットな構造は、最小量の依存関係と抽象化を備えており、発電中はより安定した結果をもたらします。モデルはこのようなコードを簡単に適応させ、コンポーネント間の予想される接続に違反することが多い場合があります。

    SDK統合 – 手動で信頼できる

    ほとんどの言語モデルは、時代遅れのバージョンのドキュメントでトレーニングされています。したがって、SDKをインストールするための手順を生成する場合、エラーがよく表示されます。時代遅れのコマンド、無関係なパラメーター、またはアクセスできないリソースへのリンク。練習のショー:公式のドキュメントと手動チューニングを使用して、LLMを補助的な役割にします。たとえば、テンプレートコードや構成の適応を生成します。

    なぜ原則がまだ機能しているのか – しかし、手動開発で

    固体で乾燥した清潔から清潔からの困難は、LLMを介したコドヘゲエネーションに懸念していることを理解することが重要です。開発者がコードを手動で書き込むと、これらの原則は引き続き価値を示し続けます。つながりを低下させ、サポートを簡素化し、プロジェクトの読みやすさと柔軟性を高めます。

    これは、人間の思考が一般化する傾向があるという事実によるものです。私たちはパターンを探しています。個々のエンティティに繰り返し論理を持ち込み、パターンを作成します。おそらく、この動作には進化的なルーツがあります。情報の量を減らすと、認知リソースが節約されます。

    LLMは異なる行動をとる:彼らはデータの量から負荷を経験せず、貯蓄を努力しません。それどころか、複雑な抽象化を構築および維持するよりも、それらが重複した断片化された情報を使用して作業するのが簡単です。そのため、繰り返し構造と最小限のアーキテクチャの重大度を備えた、カプセル化なしでコードに対処する方が簡単です。

    結論

    大規模な言語モデルは、特に初期段階で、または補助コードを作成する際に、開発において有用なツールです。ただし、アプローチを適応させることが重要です。アーキテクチャを簡素化し、抽象化を制限し、複雑な依存関係を避け、SDKを構成する際にそれらに依存しないようにすることが重要です。

    固体、乾燥、きれいの原則はまだ関連していますが、人の手に最善の効果をもたらします。 LLMで作業する場合、手動で簡単に完成できる信頼できる理解可能なコードを取得できる簡素化された実用的なスタイルを使用することが合理的です。そして、LLMが忘れる場所 – コードの複製は彼が覚えておくのに役立ちます。

    Surreal Engine C++ を WebAssembly に移植

    この投稿では、Surreal Engine ゲーム エンジンを WebAssembly に移植する方法について説明します。

    シュール エンジン – Unreal Engine 1 の機能のほとんどを実装するゲーム エンジン、このエンジン上の有名なゲーム -アンリアル トーナメント 99、アンリアル、デウス エクス、アンダイング。これは、主にシングルスレッド実行環境で動作する古典的なエンジンを指します。

    当初、私は、妥当な時間内に完了できないプロジェクトに挑戦して、Twitch フォロワーに私でも実行できないプロジェクトがあることを示すことを考えていました。最初のストリーム中に、Emscripten を使用して Surreal Engine C++ を WebAssembly に移植するタスクが実行可能であることに突然気づきました。

    Surreal Engine Emscripten Demo

    1 か月後には、WebAssembly でフォークとエンジンのアセンブリをデモンストレーションできるようになります。
    https://demensdeum.com/demos/SurrealEngine/

    オリジナルと同様に、コントロールはキーボードの矢印を使用して実行されます。次に、これをモバイル コントロール (タチ) に適応させ、Unreal Championship 99 レンダリングの正しいライティングやその他のグラフィック機能を追加する予定です。

    どこから始めればよいですか?

    最初に言いたいのは、Emscripten を使用すると、どのプロジェクトでも C++ から WebAssembly に移植できるということです。唯一の問題は、機能がどの程度完成するかということです。 Surreal Engine の場合は、ライブラリ ポートがすでに Emscripten で利用できるプロジェクトを選択してください。エンジンは SDL 2、OpenAL – ライブラリを使用します。どちらも Emscripten に移植されています。ただし、Vulkan はグラフィックス API として使用されており、現在 HTML5 では利用できません。WebGPU の実装作業が進行中ですが、これもドラフト段階にあり、Vulkan から WebGPU へのさらなる移植がどれほど簡単になるかも不明です。完全に標準化された後。したがって、Surreal Engine 用に独自の基本的な OpenGL-ES / WebGL レンダリングを作成する必要がありました。

    プロジェクトのビルド

    Surreal Engine でシステムを構築 – CMake も移植を簡素化します。 Emscripten はネイティブ ビルダーを提供します。えむけ、えむけ

    Surreal Engine の移植は、Death-Mask と呼ばれる、WebGL/OpenGL ES および C++ で作成した最新ゲームのコードに基づいていました。そのため、開発ははるかに簡単で、必要なビルド フラグとコード サンプルがすべて揃っていました。

    CMakeLists.txt の最も重要なポイントの 1 つは Emscripten のビルド フラグです。以下はプロジェクト ファイルの例です。

    
    -s MAX_WEBGL_VERSION=2 \
    
    -s EXCEPTION_DEBUG \
    
    -fexceptions \
    
    --preload-file UnrealTournament/ \
    
    --preload-file SurrealEngine.pk3 \
    
    --bind \
    
    --use-preload-plugins \
    
    -Wall \
    
    -Wextra \
    
    -Werror=return-type \
    
    -s USE_SDL=2 \
    
    -s ASSERTIONS=1 \
    
    -w \
    
    -g4 \
    
    -s DISABLE_EXCEPTION_CATCHING=0 \
    
    -O3 \
    
    --no-heap-copy \
    
    -s ALLOW_MEMORY_GROWTH=1 \
    
    -s EXIT_RUNTIME=1")
    
    

    ビルド スクリプト自体:

    
    emmake make -j 16
    
    cp SurrealEngine.data /srv/http/SurrealEngine/SurrealEngine.data
    
    cp SurrealEngine.js /srv/http/SurrealEngine/SurrealEngine.js
    
    cp SurrealEngine.wasm /srv/http/SurrealEngine/SurrealEngine.wasm
    
    cp ../buildScripts/Emscripten/index.html /srv/http/SurrealEngine/index.html
    
    cp ../buildScripts/Emscripten/background.png /srv/http/SurrealEngine/background.png
    
    

    次に、インデックスを準備します。 .html 、プロジェクト ファイル システム プリローダーが含まれています。 Web にアップロードするために、Unreal Championship Demo バージョン 338 を使用しました。CMake ファイルからわかるように、解凍されたゲーム フォルダーがビルド ディレクトリに追加され、Emscripten のプリロード ファイルとしてリンクされました。

    メインコードの変更

    その後、ゲームのゲーム ループを変更する必要がありました。無限ループを実行することはできません。これによりブラウザがフリーズします。代わりに emscripten_set_main_loop を使用する必要があります。この機能については 2017 年のメモ「< a href="https://demensdeum.com /blog/ru/2017/03/29/porting-sdl-c-game-to-html5-emscripten/" rel="noopener" target="_blank">SDL C++ ゲームを HTML5 (Emscripten) に移植」
    while ループを終了するコードを if に変更し、ゲーム ループを含むゲーム エンジンのメイン クラスをグローバル スコープに表示し、グローバル オブジェクトからゲーム ループ ステップを呼び出すグローバル関数を作成します。 :

    
    #include <emscripten.h>
    
    Engine *EMSCRIPTEN_GLOBAL_GAME_ENGINE = nullptr;
    
    void emscripten_game_loop_step() {
    
    	EMSCRIPTEN_GLOBAL_GAME_ENGINE->Run();
    
    }
    
    #endif
    
    

    この後、アプリケーションにバックグラウンド スレッドがないことを確認する必要があります。存在する場合は、シングル スレッド実行用に書き直すか、Emscripten の phtread ライブラリを使用する準備をします。
    Surreal Engine のバックグラウンド スレッドは音楽の再生に使用されます。データは、現在のトラック、音楽を再生する必要性、またはその不在に関するメイン エンジン スレッドから取得され、バックグラウンド スレッドはミューテックス経由で新しい状態を受け取り、新しい音楽の再生を開始します。 、または一時停止します。バックグラウンド ストリームは、再生中に音楽をバッファリングするためにも使用されます。
    pthread を使用して Emscripten 用の Surreal Engine を構築しようとした試みは失敗しました。SDL2 ポートと OpenAL ポートは pthread サポートなしで構築されており、音楽のために再構築したくなかったためです。そこで、BGM ストリームの機能をループを使用したシングルスレッド実行に移行しました。 C++ コードから pthread 呼び出しを削除することで、バッファリングと音楽再生をメイン スレッドに移動し、遅延が発生しないように、バッファを数秒増やしました。

    次に、グラフィックとサウンドの具体的な実装について説明します。

    Vulkan はサポートされていません!

    はい、Vulkan は HTML5 ではサポートされていませんが、すべてのマーケティング パンフレットでは Vulkan の主な利点としてクロスプラットフォームおよび広範なプラットフォームのサポートが紹介されています。このため、簡素化された OpenGL タイプの基本的なグラフィックス レンダラーを独自に作成する必要がありました。 ES はモバイル デバイスで使用され、最新の OpenGL のファッショナブルな機能が含まれていない場合もありますが、WebGL に非常によく移植されており、まさに Emscripten が実装しているものです。基本的なタイル レンダリング、最も単純な GUI 表示用の bsp レンダリング、およびモデルとマップのレンダリングの作成は 2 週間で完了しました。おそらくこれがこのプロジェクトで最も困難な部分でした。 Surreal Engine レンダリングの全機能を実装するには、まだ多くの作業が必要です。そのため、読者からのコードやプル リクエストの形での支援を歓迎します。

    OpenAL がサポートされています!

    幸運なことに、Surreal Engine はオーディオ出力に OpenAL を使用しています。 OpenAL で簡単な Hello World を記述し、Emscripten を使用して WebAssembly で組み立てたので、すべてがいかに単純であるかが明らかになり、サウンドの移植に着手しました。
    数時間のデバッグ後、Emscripten の OpenAL 実装にはいくつかのバグがあることが明らかになりました。たとえば、モノラル チャネル数の読み取りを初期化するときに、メソッドが無限の数を返し、無限サイズのベクトルを初期化しようとした後、C++例外vector::length_errorでクラッシュします。
    >
    モノラル チャンネルの数を 2048 にハードコーディングすることで、この問題を回避することができました。

    
    		alcGetIntegerv(alDevice, ALC_STEREO_SOURCES, 1, &stereoSources);
    
    
    
    #if __EMSCRIPTEN__
    
    		monoSources = 2048; // for some reason Emscripten's OpenAL gives infinite monoSources count, bug?
    
    #endif
    
    
    
    

    ネットワークはありますか?

    Surreal Engine は現在オンライン プレイをサポートしていません。ボットでのプレイはサポートされていますが、これらのボット用の AI を作成する人が必要です。理論的には、Websocket を使用して WebAssembly/Emscripten にネットワーク ゲームを実装できます。

    結論

    結論として、Emscripten ポートがあるライブラリの使用と、WebAssembly 用に C++ でゲームを実装した過去の経験のおかげで、Surreal Engine の移植は非常にスムーズに完了したと言いたいと思います。エムスクリプテンで。以下は、このトピックに関する知識源とリポジトリへのリンクです。
    マ、マ、マ、モンスターを倒せ!

    また、できれば WebGL/OpenGL ES レンダリング コードでプロジェクトを支援したい場合は、Telegram で私に連絡してください。
    https://t.me/demenscave

    リンク

    https://demensdeum.com/demos/Sur​​realEngine/
    https://github.com/demensdeum/SurrealEngine-Emscripten

    https://github.com/dpjudas/SurrealEngine

    macOS で USB キーボードのバックライトをオンにする

    最近、RGB バックライト付きの非常に安価な Getorix GK-45X USB キーボードを購入しました。 M1 プロセッサを搭載した MacBook Pro に接続してみたところ、RGB バックライトが機能しないことが明らかになりました。 Fn + Scroll Lock の魔法の組み合わせを押してもバックライトは点灯せず、MacBook 画面のバックライト レベルのみが変化しました。
    この問題にはいくつかの解決策があります。OpenRGB (機能しない)、HID LED テスト (機能しない) です。 kvmswitch ユーティリティのみが機能しました:
    https://github.com/stoutput/OSX-KVM

    GitHub からダウンロードし、システム設定のセキュリティ パネルでターミナルからの実行を許可する必要があります。
    説明からわかるように、ユーティリティを起動すると、Fn + Scroll Lock キーが送信され、キーボードのバックライトがオン/オフになります。

    ツリーソート

    ツリーソート – 二分探索ツリーを使用したソート。時間計算量 – O(n²)。このようなツリーでは、左側の各ノードにはノードより小さい番号があり、右側にはノードより大きい番号があります。ルートから来て左から右に値を出力すると、ソートされた番号のリストが得られます。 。驚くべきことですね?

    二分探索ツリー図を考えてみましょう。

    デリック・クッツェー (パブリック ドメイン)

    左下の各ノード、つまり右側のノードごとに、左下隅の最後から 2 番目の左ノードから数字を手動で読み取ってみてください。

    次のようになります:

    <オル>

  • 左下の最後から2番目のノード – 3.
  • 左分岐があります – 1.
  • この番号 (1) を選択してください
  • 次に頂点 3 (1, 3) を取得します
  • 右側はブランチ 6 ですが、ブランチが含まれています。したがって、同じように読みます。
  • ノード 6 の左分岐 4 (1、3、4)
  • ノード自体は 6 (1、3、4、6) です
  • 右 7 (1、3、4、6、7)
  • ルート ノードに移動します – 8 (1、3、4、6、7、8)
  • 類推により、右側にすべてを出力します
  • 最終的なリストを取得します – 1、3、4、6、7、8、10、13、14
  • コードでアルゴリズムを実装するには、次の 2 つの関数が必要です。

    <オル>

  • 二分探索ツリーの組み立て
  • 二分探索ツリーを正しい順序で出力する
  • 二分探索木は読み取られたときと同じ方法で組み立てられ、それが大きいか小さいかに応じて、左側または右側の各ノードに番号が付けられます。

    Lua での例:

    
    function Node:new(value, lhs, rhs)
        output = {}
        setmetatable(output, self)
        self.__index = self  
        output.value = value
        output.lhs = lhs
        output.rhs = rhs
        output.counter = 1
        return output  
    end
    
    function Node:Increment()
        self.counter = self.counter + 1
    end
    
    function Node:Insert(value)
        if self.lhs ~= nil and self.lhs.value > value then
            self.lhs:Insert(value)
            return
        end
    
        if self.rhs ~= nil and self.rhs.value < value then
            self.rhs:Insert(value)
            return
        end
    
        if self.value == value then
            self:Increment()
            return
        elseif self.value > value then
            if self.lhs == nil then
                self.lhs = Node:new(value, nil, nil)
            else
                self.lhs:Insert(value)
            end
            return
        else
            if self.rhs == nil then
                self.rhs = Node:new(value, nil, nil)
            else
                self.rhs:Insert(value)
            end
            return
        end
    end
    
    function Node:InOrder(output)
        if self.lhs ~= nil then
           output = self.lhs:InOrder(output)
        end
        output = self:printSelf(output)
        if self.rhs ~= nil then
            output = self.rhs:InOrder(output)
        end
        return output
    end
    
    function Node:printSelf(output)
        for i=0,self.counter-1 do
            output = output .. tostring(self.value) .. " "
        end
        return output
    end
    
    function PrintArray(numbers)
        output = ""
        for i=0,#numbers do
            output = output .. tostring(numbers[i]) .. " "
        end    
        print(output)
    end
    
    function Treesort(numbers)
        rootNode = Node:new(numbers[0], nil, nil)
        for i=1,#numbers do
            rootNode:Insert(numbers[i])
        end
        print(rootNode:InOrder(""))
    end
    
    
    numbersCount = 10
    maxNumber = 9
    
    numbers = {}
    
    for i=0,numbersCount-1 do
        numbers[i] = math.random(0, maxNumber)
    end
    
    PrintArray(numbers)
    Treesort(numbers)

    Важный нюанс что для чисел которые равны вершине придумано множество интересных механизмов подцепления к ноде, я же просто добавил счетчик к классу вершины, при распечатке числа возвращаются по счетчику.

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/treesort

    Источники

    TreeSort Algorithm Explained and Implemented with Examples in Java | Sorting Algorithms | Geekific – YouTube

    Tree sort – YouTube

    Convert Sorted Array to Binary Search Tree (LeetCode 108. Algorithm Explained) – YouTube

    Sorting algorithms/Tree sort on a linked list – Rosetta Code

    Tree Sort – GeeksforGeeks

    Tree sort – Wikipedia

    How to handle duplicates in Binary Search Tree? – GeeksforGeeks

    Tree Sort | GeeksforGeeks – YouTube

    バケットソート

    バケット並べ替え – バケットごとに並べ替えます。このアルゴリズムはカウンティングソートに似ていますが、数値が「バケット」範囲に収集され、その後、他の十分に生産性の高いソートアルゴリズムを使用してバケットがソートされ、最後のステップで「バケット」の範囲が展開される点が異なります。 1 ずつ増やすと、ソートされたリストが得られます。

    アルゴリズムの時間計算量は O(nk) です。このアルゴリズムは、一様分布の法則に従うデータに対して線形時間で機能します。簡単に言うと、要素は「スパイク」のない特定の範囲内にある必要があります (たとえば、0.0 から 1.0 までの数値)。そのような数字の中に 4 または 999 がある場合、中庭法によれば、そのような列は「偶数」とみなされなくなります。

    Julia での実装例:

        buckets = Vector{Vector{Int}}()
        
        for i in 0:bucketsCount - 1
            bucket = Vector{Int}()
            push!(buckets, bucket)
        end
    
        maxNumber = maximum(numbers)
    
        for i in 0:length(numbers) - 1
            bucketIndex = 1 + Int(floor(bucketsCount * numbers[1 + i] / (maxNumber + 1)))
            push!(buckets[bucketIndex], numbers[1 + i])
        end
    
        for i in 0:length(buckets) - 1
            bucketIndex = 1 + i
            buckets[bucketIndex] = sort(buckets[bucketIndex])
        end
    
        flat = [(buckets...)...]
        print(flat, "\n")
    
    end
    
    numbersCount = 10
    maxNumber = 10
    numbers = rand(1:maxNumber, numbersCount)
    print(numbers,"\n")
    bucketsCount = 10
    bucketSort(numbers, bucketsCount)

    На производительность алгоритма также влияет число ведер, для большего количества чисел лучше взять большее число ведер (Algorithms in a nutshell by George T. Heineman)

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/bucketSort

    Источники

    https://www.youtube.com/watch?v=VuXbEb5ywrU
    https://www.youtube.com/watch?v=ELrhrrCjDOA
    https://medium.com/karuna-sehgal/an-introduction-to-bucket-sort-62aa5325d124
    https://www.geeksforgeeks.org/bucket-sort-2/
    https://ru.wikipedia.org/wiki/%D0%91%D0%BB%D0%BE%D1%87%D0%BD%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0
    https://www.youtube.com/watch?v=LPrF9yEKTks
    https://en.wikipedia.org/wiki/Bucket_sort
    https://julialang.org/
    https://www.oreilly.com/library/view/algorithms-in-a/9780596516246/ch04s08.html

    基数ソート

    基数ソート – 基数ソート。このアルゴリズムは、要素の比較がないという点でカウント ソートに似ています。代わりに、要素が *文字ごと* に *バケット* (バケット) にグループ化され、バケットは現在の数値文字のインデックスによって選択されます。時間計算量 – O(nd)。

    次のように動作します:

    • 入力は数字 6、12、44、9 になります
    • リスト (0 ~ 9) のバケットを 10 個作成し、そこに少しずつ数字を追加/並べ替えます。

    次へ:

    <オル>

  • 数値内の最大文字数までのカウンター i でループを開始します
  • インデックス i を右から左に指定すると、各数値に対して 1 つの記号が得られ、記号がない場合は 0 であると見なされます。
  • 記号を数値に変換する
  • インデックス番号でバケットを選択し、そこに整数を入力します
  • 数値の検索が終了したら、すべてのバケットを数値のリストに変換し直します
  • ランク順に並べ替えられた数値を取得する
  • すべての数字がなくなるまで繰り返します
  • Scala での基数ソートの例:

    
    import scala.util.Random.nextInt
    
    
    
    object RadixSort {
    
        def main(args: Array[String]) = {
    
            var maxNumber = 200
    
            var numbersCount = 30
    
            var maxLength = maxNumber.toString.length() - 1
    
    
    
            var referenceNumbers = LazyList.continually(nextInt(maxNumber + 1)).take(numbersCount).toList
    
            var numbers = referenceNumbers
    
            
    
            var buckets = List.fill(10)(ListBuffer[Int]())
    
    
    
            for( i <- 0 to maxLength) { numbers.foreach( number => {
    
                        var numberString = number.toString
    
                        if (numberString.length() > i) {
    
                            var index = numberString.length() - i - 1
    
                            var character = numberString.charAt(index).toString
    
                            var characterInteger = character.toInt  
    
                            buckets.apply(characterInteger) += number
    
                        }
    
                        else {
    
                            buckets.apply(0) += number
    
                        }
    
                    }
    
                )
    
                numbers = buckets.flatten
    
                buckets.foreach(x => x.clear())
    
            }
    
            println(referenceNumbers)
    
            println(numbers)
    
            println(s"Validation result: ${numbers == referenceNumbers.sorted}")
    
        }
    
    }
    
    

    このアルゴリズムには、GPU などで並列実行するためのバージョンもあります。ちょっとした並べ替えオプションもあります。これは非常に興味深く、本当に息をのむようなものに違いありません。

    リンク

    https://gitlab .com/demensdeum/algorithms/-/blob/master/sortAlgorithms/radixSort/radixSort.scala

    ソース

    https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%80%D0%B0%D0%B7%D1%80%D1%8F%D 0%B4%D0%BD%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0% BA%D0%B0
    https://www.geeksforgeeks.org/radix-sort/

    https://www.youtube.com/watch?v=toAlAJKojos

    https://github.com/gyatskov/radix-sort

    ヒープソート

    ヒープソート – ピラミッドソート。アルゴリズムの時間計算量 – O(n log n)、速いですよね?私はこれを、落ちてくる小石の選別と呼んでいます。これを説明する最も簡単な方法は視覚的に説明することだと思います。

    入力は、次のような数値のリストです。
    5、0、7、2、3、9、4

    左から右に、データ構造、つまりバイナリ ツリー、または私がそれを呼んでいる – が作成されます。ピラミッド。ピラミッド要素には最大 2 つの子要素を含めることができますが、最上位の要素は 1 つだけです。

    バイナリ ツリーを作成しましょう:
    ⠀⠀5
    ⠀0⠀7
    2 3 9 4

    ピラミッドを長い間見てみると、これらは配列からの単なる数値であり、次々に来て、各フロアの要素の数が 2 倍になっていることがわかります。

    次に、楽しい作業が始まります。小石を落とす方法 (heapify) を使用して、ピラミッドを下から上に並べ替えましょう。最終階(2 3 9 4)から並べ替えを開始することもできますが、意味がありません。以下に転落する可能性のある床はありません。

    したがって、最後から 2 番目のフロア (0 7) から要素をドロップし始めます。
    ⠀⠀5
    ⠀0⠀7
    2 3 9 4

    最初に該当する要素が右から選択されます。この場合は 7 です。次に、その下にあるものを調べます。その下には 9 と 4 があり、9 は 4 より大きく、9 はより大きいです。セブン! 7 を 9 に落とし、9 を持ち上げて 7 の位置に置きます。
    ⠀⠀5
    ⠀0⠀9
    2 3 7 4

    次に、7 には下に落ちるところがないことがわかり、左側の最後から 2 番目の階にある番号 0 に進みます。
    ⠀⠀5
    0⠀9
    2 3 7 4

    その下にあるものを見てみましょう – 2 と 3、2 は 3 より小さく、3 は 0 より大きいので、0 と 3 を交換します。
    ⠀⠀5
    ⠀3⠀9
    2 0 7 4

    フロアの端に到達したら、上のフロアに行き、可能であればそこにすべてを落としてください。
    結果はデー​​タ構造、つまりヒープ、つまり最大ヒープになります。一番上にあるのは最大の要素です。
    ⠀⠀9
    ⠀3⠀7
    2 0 5 4

    これを配列表現に戻すと、リストが得られます。
    [9、3、7、2、0、5、4]

    このことから、最初と最後の要素を交換することで、最終的な並べ替え位置の最初の数値が得られる、つまり 9 が並べ替えられたリストの最後にあるはずであると結論付けることができ、場所を入れ替えます。
    [4、3、7、2、0、5、9]

    二分木を見てみましょう:
    ⠀⠀4
    ⠀3⠀7
    2 0 5 9

    結果は、ツリーの下の部分がソートされた状況になります。必要なのは、4 を正しい位置にドロップし、アルゴリズムを繰り返すだけです。ただし、すでにソートされている数値、つまり 9 は考慮しません。
    ⠀⠀4
    ⠀3⠀7
    2 0 5 9

    ⠀⠀7
    ⠀3⠀4
    2 0 5 9

    ⠀⠀7
    ⠀3⠀5
    2 0 4 9

    4 を落とした後、9 – の次に大きな数字を上げたことが判明しました。 7. ソートされていない最後の数字 (4) と最大の数字 (7) を交換します
    ⠀⠀4
    ⠀3⠀5
    2 0 7 9

    正しい最終位置に 2 つの数値があることがわかりました。
    4、3、5、2、0、79

    次に、既に並べ替えられたアルゴリズムを無視して並べ替えアルゴリズムを繰り返します。最終的には ヒープ タイプ:
    ⠀⠀0
    ⠀2⠀3
    4 5 7 9

    またはリストとして:
    0、2、3、4、5、7、9

    実装

    アルゴリズムは通常、次の 3 つの機能に分割されます。

    <オル>

  • ヒープの作成
  • ふるい分けアルゴリズム (heapify)
  • ソートされていない最後の要素と最初の要素を置き換えます
  • ヒープは、heapify 関数を使用してバイナリ ツリーの最後から 2 番目の行を右から左に配列の末尾まで走査することによって作成されます。次にサイクルでは、最初の数値の置換が行われ、その後、最初の要素が配置されるか、その位置に残ります。その結果、最大の要素が 1 位に配置され、参加者が 1 人減ってサイクルが繰り返されます。各パスの後、ソートされた数値がリストの最後に残ります。

    Ruby でのヒープソートの例:

    
    
    
    
    
    module Colors
    
    
    
        BLUE = "\033[94m"
    
    
    
        RED = "\033[31m"
    
    
    
        STOP = "\033[0m"
    
    
    
    end
    
    
    
    
    
    
    
    def heapsort(rawNumbers)
    
    
    
        numbers = rawNumbers.dup
    
    
    
    
    
    
    
        def swap(numbers, from, to)
    
    
    
            temp = numbers[from]
    
    
    
            numbers[from] = numbers[to]
    
    
    
            numbers[to] = temp
    
    
    
        end
    
    
    
    
    
    
    
        def heapify(numbers)
    
    
    
            count = numbers.length()
    
    
    
            lastParentNode = (count - 2) / 2
    
    
    
    
    
    
    
            for start in lastParentNode.downto(0)
    
    
    
                siftDown(numbers, start, count - 1)
    
    
    
                start -= 1 
    
    
    
            end
    
    
    
    
    
    
    
            if DEMO
    
    
    
                puts "--- heapify ends ---"
    
    
    
            end
    
    
    
        end
    
    
    
    
    
    
    
        def siftDown(numbers, start, rightBound)      
    
    
    
            cursor = start
    
    
    
            printBinaryHeap(numbers, cursor, rightBound)
    
    
    
    
    
    
    
            def calculateLhsChildIndex(cursor)
    
    
    
                return cursor * 2 + 1
    
    
    
            end
    
    
    
    
    
    
    
            def calculateRhsChildIndex(cursor)
    
    
    
                return cursor * 2 + 2
    
    
    
            end            
    
    
    
    
    
    
    
            while calculateLhsChildIndex(cursor) <= rightBound
    
    
    
                lhsChildIndex = calculateLhsChildIndex(cursor)
    
    
    
                rhsChildIndex = calculateRhsChildIndex(cursor)
    
    
    
    
    
    
    
                lhsNumber = numbers[lhsChildIndex]
    
    
    
                biggerChildIndex = lhsChildIndex
    
    
    
    
    
    
    
                if rhsChildIndex <= rightBound
    
    
    
                    rhsNumber = numbers[rhsChildIndex]
    
    
    
                    if lhsNumber < rhsNumber
    
    
    
                        biggerChildIndex = rhsChildIndex
    
    
    
                    end
    
    
    
                end
    
    
    
    
    
    
    
                if numbers[cursor] < numbers[biggerChildIndex]
    
    
    
                    swap(numbers, cursor, biggerChildIndex)
    
    
    
                    cursor = biggerChildIndex
    
    
    
                else
    
    
    
                    break
    
    
    
                end
    
    
    
                printBinaryHeap(numbers, cursor, rightBound)
    
    
    
            end
    
    
    
            printBinaryHeap(numbers, cursor, rightBound)
    
    
    
        end
    
    
    
    
    
    
    
        def printBinaryHeap(numbers, nodeIndex = -1, rightBound = -1)
    
    
    
            if DEMO == false
    
    
    
                return
    
    
    
            end
    
    
    
            perLineWidth = (numbers.length() * 4).to_i
    
    
    
            linesCount = Math.log2(numbers.length()).ceil()
    
    
    
            xPrinterCount = 1
    
    
    
            cursor = 0
    
    
    
            spacing = 3
    
    
    
            for y in (0..linesCount)
    
    
    
                line = perLineWidth.times.map { " " }
    
    
    
                spacing = spacing == 3 ? 4 : 3
    
    
    
                printIndex = (perLineWidth / 2) - (spacing * xPrinterCount) / 2
    
    
    
                for x in (0..xPrinterCount - 1)
    
    
    
                    if cursor >= numbers.length
    
    
    
                        break
    
    
    
                    end
    
    
    
                    if nodeIndex != -1 && cursor == nodeIndex
    
    
    
                        line[printIndex] = "%s%s%s" % [Colors::RED, numbers[cursor].to_s, Colors::STOP]
    
    
    
                    elsif rightBound != -1 && cursor > rightBound
    
    
    
                        line[printIndex] = "%s%s%s" % [Colors::BLUE, numbers[cursor].to_s, Colors::STOP]
    
    
    
                    else
    
    
    
                        line[printIndex] = numbers[cursor].to_s
    
    
    
                    end
    
    
    
                    cursor += 1
    
    
    
                    printIndex += spacing
    
    
    
                end
    
    
    
                print line.join()
    
    
    
                xPrinterCount *= 2           
    
    
    
                print "\n"            
    
    
    
            end
    
    
    
        end
    
    
    
    
    
    
    
        heapify(numbers)
    
    
    
        rightBound = numbers.length() - 1
    
    
    
    
    
    
    
        while rightBound > 0
    
    
    
            swap(numbers, 0, rightBound)   
    
    
    
            rightBound -= 1
    
    
    
            siftDown(numbers, 0, rightBound)     
    
    
    
        end
    
    
    
    
    
    
    
        return numbers
    
    
    
    end
    
    
    
    
    
    
    
    numbersCount = 14
    
    
    
    maximalNumber = 10
    
    
    
    numbers = numbersCount.times.map { Random.rand(maximalNumber) }
    
    
    
    print numbers
    
    
    
    print "\n---\n"
    
    
    
    
    
    
    
    start = Time.now
    
    
    
    sortedNumbers = heapsort(numbers)
    
    
    
    finish = Time.now
    
    
    
    heapSortTime = start - finish
    
    
    
    
    
    
    
    start = Time.now
    
    
    
    referenceSortedNumbers = numbers.sort()
    
    
    
    finish = Time.now
    
    
    
    referenceSortTime = start - finish
    
    
    
    
    
    
    
    print "Reference sort: "
    
    
    
    print referenceSortedNumbers
    
    
    
    print "\n"
    
    
    
    print "Reference sort time: %f\n" % referenceSortTime
    
    
    
    print "Heap sort:      "
    
    
    
    print sortedNumbers
    
    
    
    print "\n"
    
    
    
    if DEMO == false
    
    
    
        print "Heap sort time:      %f\n" % heapSortTime
    
    
    
    else
    
    
    
        print "Disable DEMO for performance measure\n"
    
    
    
    end
    
    
    
    
    
    
    
    if sortedNumbers != referenceSortedNumbers
    
    
    
        puts "Validation failed"
    
    
    
        exit 1
    
    
    
    else
    
    
    
        puts "Validation success"
    
    
    
        exit 0
    
    
    
    end
    
    
    
    

    このアルゴリズムは視覚化しないと理解しにくいため、最初にバイナリ ツリーの現在のビューを出力する関数を作成することをお勧めします。

    リンク

    https://gitlab.com/demensdeum/algorithms/-/blob/master/sortAlgorithms/heapsort/heapsort.rb

    ソース

    http://rosettacode.org/wiki/Sorting_algorithms/Heapsort
    https://www.youtube.com/watch?v=LbB357_RwlY

    https://habr.com/ru/company/ otus/blog/460087/

    https://ru.wikipedia.org/wiki/Pyramid_sort

    https://neerc.ifmo.ru/wiki /index.php?title=ヒープソート

    https://wiki5.ru/wiki/Heapsort

    https://wiki.c2.com/?HeapSort

    https://ru.wikipedia.org/wiki/Tree (データ構造)

    https://ru.wikipedia.org/wiki/Heap (データ構造)

    https://www.youtube.com/watch?v=2DmK_H7IdTo

    https://www.youtube.com/watch?v=kU4KBD4NFtw

    https://www.youtube.com/watch?v=DU1uG5310x0

    https://www.youtube.com/watch?v =BzQGPA_v-vc

    https://www.geeksforgeeks.org/バイナリヒープの配列表現/

    https://habr.com/ru/post/112222/

    https://www.cs.usfca. edu/~galles/visualization/BST.html

    https://www.youtube.com/watch?v=EQzqHWtsKq4

    https://medium.com/@dimko1/%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC% D1 %8B-%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8-heapsort-796ba965018b

    https://ru.wikibrief.org/wiki/Heapsort

    https://www.youtube.com/watch?v=GUUpmrTnNbw

    Bumblebee All Troubles

    Recently, it turned out that users of modern Nvidia GPUs under Arch Linux do not need to use the bumblebee package at all, for example, for me it did not detect an external monitor when connected. I recommend removing the bumblebee package and all related packages, and installing prime using the instructions on the Arch Wiki.
    Next, to launch all games on Steam and 3D applications, add prime-run, for Steam this is done like this prime-run %command% in additional launch options.
    To check the correctness, you can use glxgears, prime-run glxgears.
    https://bbs.archlinux.org/viewtopic.php? pid=2048195#p2048195

    クイックソート

    クイックソートは分割統治型の並べ替えアルゴリズムです。再帰的に、部分ごとに数値の配列を解析し、選択した参照要素から小さい順に大きい順に数値を配置し、参照要素自体をそれらの間のカットオフに挿入します。何度か再帰的に繰り返すと、ソートされたリストが完成します。時間計算量 O(n2)。

    スキーム:

    <オル>

  • まず、外側から要素のリスト、つまり並べ替え境界を取得します。最初のステップでは、並べ替えの境界は最初から最後までになります。
  • 開始境界と終了境界が交差していないことを確認し、交差している場合は終了します。
  • リストから要素を選択し、それをピボットと呼びます
  • 邪魔にならないように、最後のインデックスまでに右に移動してください
  • ゼロに等しい *小さい数値* のカウンターを作成します
  • リストを左から右に、参照要素が配置されている最後のインデックスまでループします
  • 各要素を参照要素と比較します
  • それが参照値よりも小さい場合は、より小さい数値のカウンターのインデックスに従ってそれを交換します。より小さい数値のカウンタをインクリメントします。
  • ループがサポート要素に到達すると、停止し、より小さい数値のカウンターに従ってサポート要素を要素と交換します。
  • リストの左側の小さい部分と、リストの右側の大きい部分に対してアルゴリズムを別々に実行します。
  • その結果、ポイント 2 のチェックインにより、すべての再帰的反復が停止し始める
  • ソートされたリストを取得する
  • クイックソートは、モスクワ州立大学の科学者チャールズ アンソニー リチャード ホアによって発明されました。彼はロシア語を学び、コルモゴロフ学校でコンピューター翻訳と確率論を学びました。 1960 年、政治危機のため、彼はソ連を去りました。

    Rust での実装例:

    
    use rand::Rng;
    
    fn swap(numbers: &mut [i64], from: usize, to: usize) {
        let temp = numbers[from];
        numbers[from] = numbers[to];
        numbers[to] = temp;
    }
    
    fn quicksort(numbers: &mut [i64], left: usize, right: usize) {
        if left >= right {
            return
        }
        let length = right - left;
        if length <= 1 {
            return
        }
        let pivot_index = left + (length / 2);
        let pivot = numbers[pivot_index];
    
        let last_index = right - 1;
        swap(numbers, pivot_index, last_index);
    
        let mut less_insert_index = left;
    
        for i in left..last_index {
            if numbers[i] < pivot {
                swap(numbers, i, less_insert_index);
                less_insert_index += 1;
            }
        }
        swap(numbers, last_index, less_insert_index);
        quicksort(numbers, left, less_insert_index);
        quicksort(numbers, less_insert_index + 1, right);
    }
    
    fn main() {
        let mut numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let mut reference_numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    
        let mut rng = rand::thread_rng();
        for i in 0..numbers.len() {
            numbers[i] = rng.gen_range(-10..10);
            reference_numbers[i] = numbers[i];
        }
    
        reference_numbers.sort();
    
      println!("Numbers           {:?}", numbers);
      let length = numbers.len();
      quicksort(&mut numbers, 0, length);
      println!("Numbers           {:?}", numbers);
      println!("Reference numbers {:?}", reference_numbers);
    
      if numbers != reference_numbers {
        println!("Validation failed");
        std::process::exit(1);
      }
      else {
        println!("Validation success!");
        std::process::exit(0);
      }
    }
    

    何も明確でない場合は、サンディエゴ大学の Rob Edwards によるビデオを見ることをお勧めします https://www.youtube.com/watch?v=ZHVk2blR45Q アルゴリズムの本質と実装をステップごとに最も簡単に示しています。

    リンク

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/quickSort

    ソース

    https://www.youtube.com/watch?v =4s-aG6yGGLU
    https://www.youtube.com/watch?v=ywWBy6J5gz8
    https://www.youtube.com/watch?v=Hoixgm4-P4M
    https://ru.wikipedia.org/wiki/Быстрая_сортировка
    https://www.youtube.com/watch?v=Hoixgm4-P4M
    https://www.youtube.com/watch?v=XE4VP_8Y0BU
    https://www.youtube.com/watch?v=MZaf_9IZCrc
    https://www.youtube.com/watch?v=ZHVk2blR45Q
    http://rosettacode.org/wiki/Sorting_algorithms/Quicksort
    https://www.youtube.com/watch?v=4s-aG6yGGLU
    https://www.youtube.com/watch?v=dQw4w9WgXcQ
    https://www.youtube.com/watch?v=maibrCbZWKw
    https://www.geeksforgeeks.org/quick-sort/
    https://www.youtube.com/watch?v=uXBnyYuwPe8

    バイナリ挿入ソート

    二分挿入ソートは、二分検索を使用して挿入位置を決定する挿入ソートの変形です。アルゴリズムの時間計算量は O(n2) です

    アルゴリズムは次のように機能します:

    <オル>

  • ループは 0 からリストの最後まで始まります
  • ループ内で、並べ替えのために数値が選択され、その数値は別の変数に保存されます
  • 二分検索では、左側の数値に対してこの数値を挿入するためのインデックスが検索されます
  • インデックスが見つかると、挿入インデックスから開始して、左側の数字が 1 つ右にシフトされます。この過程で、並べ替える必要がある数値は消去されます。
  • 以前に保存した番号が挿入インデックスに挿入されます
  • ループの最後で、リスト全体が並べ替えられます
  • バイナリ検索中に、数値が見つからず、インデックスが返されない可能性があります。二分検索の特殊性により、検索された数値に最も近い数値が検索され、インデックスを返すには、そのインデックスを求めた数値と比較する必要があります。求めた数値が小さい場合、求めた数値は次の位置にある必要があります。インデックスが左側にあり、それ以上の場合は右側にあります。

    Go コード:

    
    import (
    	"fmt"
    	"math/rand"
    	"time"
    )
    
    const numbersCount = 20
    const maximalNumber = 100
    
    func binarySearch(numbers []int, item int, low int, high int) int {
    	for high > low {
    		center := (low + high) / 2
    		if numbers[center] < item { low = center + 1 } else if numbers[center] > item {
    			high = center - 1
    		} else {
    			return center
    		}
    	}
    
    	if numbers[low] < item {
    		return low + 1
    	} else {
    		return low
    	}
    }
    
    func main() {
    	rand.Seed(time.Now().Unix())
    	var numbers [numbersCount]int
    	for i := 0; i < numbersCount; i++ {
    		numbers[i] = rand.Intn(maximalNumber)
    	}
    	fmt.Println(numbers)
    
    	for i := 1; i < len(numbers); i++ { searchAreaLastIndex := i - 1 insertNumber := numbers[i] insertIndex := binarySearch(numbers[:], insertNumber, 0, searchAreaLastIndex) for x := searchAreaLastIndex; x >= insertIndex; x-- {
    			numbers[x+1] = numbers[x]
    		}
    		numbers[insertIndex] = insertNumber
    	}
    	fmt.Println(numbers)
    }
    

    リンク

    https://gitlab.com/demensdeum/algorithms/-/blob/master/sortAlgorithms/binaryInsertionSort/binaryInsertionSort.go

    ソース

    https://www.geeksforgeeks.org/binary-insertion-並べ替え/
    https://www.youtube.com/watch?v=-OVB5pOZJug

    シェルソート

    シェル ソート – 数値の配列を事前に組み合わせた挿入ソートの変形です。

    挿入ソートがどのように機能するかを覚えておく必要があります。

    1.ループは0からループの終わりまで開始されるため、配列は2つの部分に分割されます
    2. 左側の部分では、2 番目のループが開始され、要素を右から左に比較します。左側の小さい要素が見つかるまで、右側の小さい要素が削除されます。
    3. 両方のループの最後に、ソートされたリストが得られます。

    昔々、コンピューター科学者のドナルド シェルは、挿入ソート アルゴリズムを改善する方法を考えていました。彼はまた、最初に配列を 2 サイクルで処理するが、一定の距離を置き、通常の挿入ソート アルゴリズムに変わるまで「コーム」を徐々に減らしていくというアイデアも思いつきました。すべては非常に単純で、落とし穴はありません。上記の 2 つのサイクルに別のサイクルを追加し、「コーム」のサイズを徐々に小さくします。必要なのは、比較するときに配列を超えないよう距離を確認することだけです。

    本当に興味深いトピックは、最初のループの各反復で比較長を変更するためのシーケンスを選択することです。アルゴリズムのパフォーマンスがアルゴリズムに依存するという理由から、これは興味深いものです。

    既知のオプションと時間計算量の表は、https: //en .wikipedia.org/wiki/Shellsort#Gap_sequences

    理想的な距離の計算にはさまざまな人々が関与しており、このトピックは彼らにとって非常に興味深いものだったようです。 Ruby を実行して、最速の sort() アルゴリズムを呼び出すことはできないでしょうか?

    一般に、これらの奇妙な人々は、シェル アルゴリズムの「櫛」の距離/ギャップの計算をテーマに論文を書きました。私は彼らの研究結果を単純に使用し、Hibbard、Knuth-Pratt、Chiura、Sedgwick の 5 種類のシーケンスをチェックしました。

    import time
    import random
    from functools import reduce
    import math
    
    DEMO_MODE = False
    
    if input("Demo Mode Y/N? ").upper() == "Y":
        DEMO_MODE = True
    
    class Colors:
        BLUE = '\033[94m'
        RED = '\033[31m'
        END = '\033[0m'
    
    def swap(list, lhs, rhs):
        list[lhs], list[rhs] = list[rhs], list[lhs]
        return list
    
    def colorPrintoutStep(numbers: List[int], lhs: int, rhs: int):
        for index, number in enumerate(numbers):
            if index == lhs:
                print(f"{Colors.BLUE}", end = "")
            elif index == rhs:
                print(f"{Colors.RED}", end = "")
            print(f"{number},", end = "")
            if index == lhs or index == rhs:
                print(f"{Colors.END}", end = "")
            if index == lhs or index == rhs:
                print(f"{Colors.END}", end = "")
        print("\n")
        input(">")
    
    def ShellSortLoop(numbers: List[int], distanceSequence: List[int]):
        distanceSequenceIterator = reversed(distanceSequence)
        while distance:= next(distanceSequenceIterator, None):
            for sortArea in range(0, len(numbers)):
                for rhs in reversed(range(distance, sortArea + 1)):
                    lhs = rhs - distance
                    if DEMO_MODE:
                        print(f"Distance: {distance}")
                        colorPrintoutStep(numbers, lhs, rhs)
                    if numbers[lhs] > numbers[rhs]:
                        swap(numbers, lhs, rhs)
                    else:
                        break
    
    def ShellSort(numbers: List[int]):
        global ShellSequence
        ShellSortLoop(numbers, ShellSequence)
    
    def HibbardSort(numbers: List[int]):
        global HibbardSequence
        ShellSortLoop(numbers, HibbardSequence)
    
    def ShellPlusKnuttPrattSort(numbers: List[int]):
        global KnuttPrattSequence
        ShellSortLoop(numbers, KnuttPrattSequence)
    
    def ShellPlusCiuraSort(numbers: List[int]):
        global CiuraSequence
        ShellSortLoop(numbers, CiuraSequence)
    
    def ShellPlusSedgewickSort(numbers: List[int]):
        global SedgewickSequence
        ShellSortLoop(numbers, SedgewickSequence)
    
    def insertionSort(numbers: List[int]):
        global insertionSortDistanceSequence
        ShellSortLoop(numbers, insertionSortDistanceSequence)
    
    def defaultSort(numbers: List[int]):
        numbers.sort()
    
    def measureExecution(inputNumbers: List[int], algorithmName: str, algorithm):
        if DEMO_MODE:
            print(f"{algorithmName} started")
        numbers = inputNumbers.copy()
        startTime = time.perf_counter()
        algorithm(numbers)
        endTime = time.perf_counter()
        print(f"{algorithmName} performance: {endTime - startTime}")
    
    def sortedNumbersAsString(inputNumbers: List[int], algorithm) -> str:
        numbers = inputNumbers.copy()
        algorithm(numbers)
        return str(numbers)
    
    if DEMO_MODE:
        maximalNumber = 10
        numbersCount = 10
    else:
        maximalNumber = 10
        numbersCount = random.randint(10000, 20000)
    
    randomNumbers = [random.randrange(1, maximalNumber) for i in range(numbersCount)]
    
    ShellSequenceGenerator = lambda n: reduce(lambda x, _: x + [int(x[-1]/2)], range(int(math.log(numbersCount, 2))), [int(numbersCount / 2)])
    ShellSequence = ShellSequenceGenerator(randomNumbers)
    ShellSequence.reverse()
    ShellSequence.pop()
    
    HibbardSequence = [
        0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095,
        8191, 16383, 32767, 65535, 131071, 262143, 524287, 1048575,
        2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727,
        268435455, 536870911, 1073741823, 2147483647, 4294967295, 8589934591
    ]
    
    KnuttPrattSequence = [
        1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 88573, 265720, 
        797161, 2391484, 7174453, 21523360, 64570081, 193710244, 581130733, 
        1743392200, 5230176601, 15690529804, 47071589413
    ]
    
    CiuraSequence = [
                1, 4, 10, 23, 57, 132, 301, 701, 1750, 4376, 
                10941, 27353, 68383, 170958, 427396, 1068491, 
                2671228, 6678071, 16695178, 41737946, 104344866, 
                260862166, 652155416, 1630388541
    ]
    
    SedgewickSequence = [
                1, 5, 19, 41, 109, 209, 505, 929, 2161, 3905,
                8929, 16001, 36289, 64769, 146305, 260609, 587521, 
                1045505, 2354689, 4188161, 9427969, 16764929, 37730305, 
                67084289, 150958081, 268386305, 603906049, 1073643521, 
                2415771649, 4294770689, 9663381505, 17179475969
    ]
    
    insertionSortDistanceSequence = [1]
    
    algorithms = {
        "Default Python Sort": defaultSort,
        "Shell Sort": ShellSort,
        "Shell + Hibbard" : HibbardSort,
        "Shell + Prat, Knutt": ShellPlusKnuttPrattSort,
        "Shell + Ciura Sort": ShellPlusCiuraSort,
        "Shell + Sedgewick Sort": ShellPlusSedgewickSort,
        "Insertion Sort": insertionSort
    }
    
    for name, algorithm in algorithms.items():
        measureExecution(randomNumbers, name, algorithm)
    
    reference = sortedNumbersAsString(randomNumbers, defaultSort)
    
    for name, algorithm in algorithms.items():
        if sortedNumbersAsString(randomNumbers, algorithm) != reference:
            print("Sorting validation failed")
            exit(1)
    
    print("Sorting validation success")
    exit(0)
    

    私の実装では、ランダムな数値セットの場合、最も速いギャップはセジウィックとヒバードです。

    マイピー

    Python 3 – の静的型付けアナライザーについても触れておきたいと思います。マイピー。動的型付けを使用する言語に固有の問題に対処するのに役立ちます。つまり、不必要な場所に何かが貼り付けられる可能性を排除します。

    経験豊富なプログラマーが言うように、「専門家のチームがいる場合、静的型付けは必要ありません」。いつか私たち全員が専門家になり、完全に統一してマシンを理解しながらコードを書くようになりますが、今のところは同様のユーティリティを使用できます。静的型付け言語。

    リンク

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/shellSort
    http://mypy-lang.org/

    ソース

    https://dl.acm.org/doi/10.1145/368370.368387
    https://en.wikipedia.org/wiki/Shellsort
    http://rosettacode.org/wiki/Sorting_algorithms/Shell_sort
    https://ru.wikipedia.org/wiki/Сортировка_Шелла
    https://neerc.ifmo.ru/wiki/index.php?title=Сортировка_Шелла
    https://twitter.com/gvanrossum/status/700741601966985216

    二重選択ソート

    Double Selection Sort – 選択ソートのサブタイプで、速度が 2 倍になるようです。バニラのアルゴリズムは、数値のリストを 2 回ループし、最小の数値を見つけて、上のレベルのループが指す現在の数値と位置を交換します。二重選択ソートでは、最小値と最大値を検索し、– より上のレベルでループが指す 2 桁を置き換えます。左右に 2 つの数字。この乱交全体は、置換される数字のカーソルがリストの中央に見つかると終了します。その結果、ソートされた数字が視覚的中心の左右に取得されます。
    アルゴリズムの時間計算量は、選択ソートと同様です。 O(n2) ですが、おそらく 30 の加速度があると思われます%。

    境界線の状態

    この段階ですでに、衝突の瞬間を想像することができます。たとえば、左カーソルの番号 (最小値) がリスト内の最大値を指しているとき、最小値が再配置され、再配置が行われます。最大数がすぐに壊れてしまいます。したがって、アルゴリズムのすべての実装には、そのようなケースのチェックとインデックスの正しいものへの置き換えが含まれます。私の実装では、1 回のチェックで十分でした。

      maximalNumberIndex = minimalNumberIndex;
    }

    Реализация на Cito

    Cito – язык либ, язык транслятор. На нем можно писать для C, C++, C#, Java, JavaScript, Python, Swift, TypeScript, OpenCL C, при этом совершенно ничего не зная про эти языки. Исходный код на языке Cito транслируется в исходный код на поддерживаемых языках, далее можно использовать как библиотеку, либо напрямую, исправив сгенеренный код руками. Эдакий Write once – translate to anything.
    Double Selection Sort на cito:

    {
        public static int[] sort(int[]# numbers, int length)
        {
            int[]# sortedNumbers = new int[length];
            for (int i = 0; i < length; i++) {
                sortedNumbers[i] = numbers[i];
            }
            for (int leftCursor = 0; leftCursor < length / 2; leftCursor++) {
                int minimalNumberIndex = leftCursor;
                int minimalNumber = sortedNumbers[leftCursor];
    
                int rightCursor = length - (leftCursor + 1);
                int maximalNumberIndex = rightCursor;
                int maximalNumber = sortedNumbers[maximalNumberIndex];
    
                for (int cursor = leftCursor; cursor <= rightCursor; cursor++) { int cursorNumber = sortedNumbers[cursor]; if (minimalNumber > cursorNumber) {
                        minimalNumber = cursorNumber;
                        minimalNumberIndex = cursor;
                    }
                    if (maximalNumber < cursorNumber) {
                        maximalNumber = cursorNumber;
                        maximalNumberIndex = cursor;
                    }
                }
    
                if (leftCursor == maximalNumberIndex) {
                    maximalNumberIndex = minimalNumberIndex;
                }
    
                int fromNumber = sortedNumbers[leftCursor];
                int toNumber = sortedNumbers[minimalNumberIndex];
                sortedNumbers[minimalNumberIndex] = fromNumber;
                sortedNumbers[leftCursor] = toNumber;
    
                fromNumber = sortedNumbers[rightCursor];
                toNumber = sortedNumbers[maximalNumberIndex];
                sortedNumbers[maximalNumberIndex] = fromNumber;
                sortedNumbers[rightCursor] = toNumber;
            }
            return sortedNumbers;
        }
    } 
    

    リンク

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/doubleSelectionSort
    https://github.com/pfusik/cito

    ソース

    https://www.researchgate.net/publication/330084245_改良_Double_Selection_Sort_using_Algorithm
    http://algolab.valemak.com/selection-double
    https://www.geeksforgeeks.org/sorting-algorithm-slightly-improves-selection-sort/

    カクテルシェーカーの並べ替え

    カクテル シェーカー ソート –双方向バブルソーティングの一種であるシェーカーソーティング。
    アルゴリズムは次のように機能します。

    <オル>

  • ループ内での最初の検索方向が選択されます (通常は左から右)
  • ループの次に、数値がペアでチェックされます
  • 次の要素の方が大きい場合、それらは交換されます
  • 終了すると、検索プロセスが方向を逆にして再び開始されます
  • 検索は順列がなくなるまで繰り返されます
  • アルゴリズムの時間計算量はバブル – に似ています。 O(n2)

    PHP での実装例:

    <?php
    
    function cocktailShakeSort($numbers)
    {
        echo implode(",", $numbers),"\n";
        $direction = false;
        $sorted = false;
        do {
            $direction = !$direction;        
            $firstIndex = $direction == true ? 0 : count($numbers) - 1;
            $lastIndex = $direction == true ? count($numbers) - 1 : 0;
            
            $sorted = true;
            for (
                $i = $firstIndex;
                $direction == true ? $i < $lastIndex : $i > $lastIndex;
                $direction == true ? $i++ : $i--
            ) {
                $lhsIndex = $direction ? $i : $i - 1;
                $rhsIndex = $direction ? $i + 1 : $i;
    
                $lhs = $numbers[$lhsIndex];
                $rhs = $numbers[$rhsIndex];
    
                if ($lhs > $rhs) {
                    $numbers[$lhsIndex] = $rhs;
                    $numbers[$rhsIndex] = $lhs;
                    $sorted = false;
                }
            }
        } while ($sorted == false);
    
        echo implode(",", $numbers);
    }
    
    $numbers = [2, 1, 4, 3, 69, 35, 55, 7, 7, 2, 6, 203, 9];
    cocktailShakeSort($numbers);
    
    ?>

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/blob/master/sortAlgorithms/cocktailShakerSort/cocktailShakerSort.php

    Источники

    https://www.youtube.com/watch?v=njClLBoEbfI
    https://www.geeksforgeeks.org/cocktail-sort/
    https://rosettacode.org/wiki/Sorting_algorithms/Cocktail_sort

    …And Primus for All

    In this note I will describe the launch of Steam games on the Linux distribution Arch Linux in the configuration of an Intel + Nvidia laptop

    Counter-Strike: Global Offensive

    The only configuration that worked for me is Primus-vk + Vulkan.

    Install the required packages:
    pacman -S vulkan-intel lib32-vulkan-intel nvidia-utils lib32-nvidia-utils vulkan-icd-loader lib32-vulkan-icd-loader primus_vk

    Next, add launch options for Counter-Strike: Global Offensive:
    pvkrun %command% -vulkan -console -fullscreen

    Should work!

    Sid Meier’s Civilization VI

    Works in conjunction – Primus + OpenGL + LD_PRELOAD.

    Install the Primus package:
    pacman -S primus

    Next, add launch options for Sid Meier’s Civilization VI:
    LD_PRELOAD='/usr/lib/libfreetype.so.6:/usr/lib/libbrotlicommon.so.1:/usr/lib/libbrotlidec.so.1' primusrun %command%

    LD_PRELOAD pushes the Freetype compression and font libraries.

    Dota 2

    Works in conjunction – Primus + OpenGL + removal of locks at startup.

    Install the Primus package:
    pacman -S primus

    Next, add launch options for Dota 2:
    primusrun %command% -gl -console

    If the game doesn’t start with fcntl(5) for /tmp/source_engine_2808995433.lock failed, then try deleting the /tmp/source_engine_2808995433.lock file
    rm /tmp/source_engine_2808995433.lock
    Usually the lock file is left over from the last game session unless the game was closed naturally.

    How to check?

    The easiest way to check the launch of applications on a discrete Nvidia graphics card is through the nvidia-smi utility:

    For games on the Source engine, you can check through the game console using the mat_info command:

    References

    https://wiki.archlinux.org/title/Steam/Game-specific_troubleshooting
    https://help.steampowered.com/en/faqs/view/145A-FE54-F37B-278A
    https://bbs.archlinux.org/viewtopic.php?id=277708

    スリープソート

    睡眠並べ替え – sleep sort は、決定論的な奇妙な並べ替えアルゴリズムのもう 1 つの代表です。

    次のように動作します:

    <オル>

  • 要素のリストをループします
  • ループごとに別のスレッドが起動される
  • スレッドは一定期間スリープします –要素の値とスリープ後に出力される値
  • ループの最後で、スレッドの最長スリープが完了するまで待機し、並べ替えられたリストを表示します
  • C での睡眠ソート アルゴリズムのサンプル コード:

    #include <stdlib.h>
    #include <pthread.h>
    #include <unistd.h>
    
    typedef struct {
        int number;
    } ThreadPayload;
    
    void *sortNumber(void *args) {
        ThreadPayload *payload = (ThreadPayload*) args;
        const int number = payload->number;
        free(payload);
        usleep(number * 1000);
        printf("%d ", number);
        return NULL;
    }
    
    int main(int argc, char *argv[]) {
        const int numbers[] = {2, 42, 1, 87, 7, 9, 5, 35};
        const int length = sizeof(numbers) / sizeof(int);
    
        int maximal = 0;
        pthread_t maximalThreadID;
    
        printf("Sorting: ");
        for (int i = 0; i < length; i++) { pthread_t threadID; int number = numbers[i]; printf("%d ", number); ThreadPayload *payload = malloc(sizeof(ThreadPayload)); payload->number = number;
            pthread_create(&threadID, NULL, sortNumber, (void *) payload);
            if (maximal < number) {
                maximal = number;
                maximalThreadID = threadID;
            }
        }
        printf("\n");
        printf("Sorted: ");
        pthread_join(maximalThreadID, NULL);
        printf("\n");
        return 0;
    }
    

    この実装では、マイクロ秒で usleep 関数を使用し、値に 1000 を乗算しました。ミリ秒単位
    で。アルゴリズムの時間計算量 – O(とても長い)

    リンク

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/sleepSort

    ソース

    https://codoholicconfessions.wordpress。 com/2017/05/21/strangest-sorting-algorithms/
    https://twitter.com/javascriptdaily/status/856267407106682880?lang=en
    https://stackoverflow.com/questions/6474318/what-is-the-time-complexity-of-the-sleep-sort

    スターリンソート

    スターリン ソート – sort through は、データ損失を伴う並べ替えアルゴリズムの 1 つです。
    このアルゴリズムは非常に生産的効率的で、時間計算量は O(n) です。

    次のように動作します:

    <オル>

  • 配列をループし、現在の要素と次の要素を比較します
  • 次の要素が現在の要素より小さい場合は、それを削除します
  • 結果として、O(n) でソートされた配列が得られます
  • アルゴリズムの出力の例:

    Gulag: [1, 3, 2, 4, 6, 42, 4, 8, 5, 0, 35, 10]
    Element 2 sent to Gulag
    Element 4 sent to Gulag
    Element 8 sent to Gulag
    Element 5 sent to Gulag
    Element 0 sent to Gulag
    Element 35 sent to Gulag
    Element 10 sent to Gulag
    Numbers: [1, 3, 4, 6, 42]
    Gulag: [2, 4, 8, 5, 0, 35, 10]
    

    Python 3 コード:

    gulag = []
    
    print(f"Numbers: {numbers}")
    print(f"Gulag: {numbers}")
    
    i = 0
    maximal = numbers[0]
    
    while i < len(numbers):
        element = numbers[i]
        if maximal > element:
            print(f"Element {element} sent to Gulag")
            gulag.append(element)
            del numbers[i]
        else:
            maximal = element        
            i += 1
    
    print(f"Numbers: {numbers}")
    print(f"Gulag: {gulag}")
    

    欠点としてはデータ損失が挙げられますが、O(n) で理想的なソート済みリストを実現するためには、他にどのような方法があるでしょうか?

    リンク

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/stalinSort

    ソース

    https://github.com/gustavo-depaula/stalin-sort
    https://www.youtube.com/shorts/juRL-Xn-E00
    https://www.youtube.com/watch?v=L78i2YcyYfk

    選択範囲の並べ替え

    選択並べ替え –選択ソートアルゴリズム。何を選ぶの?でも最低数!!!
    アルゴリズムの時間計算量 – O(n2)

    アルゴリズムは次のように機能します。

    <オル>

  • 配列を左から右にループ処理し、現在の開始インデックスとインデックスの番号を覚えておきます。番号 A とします
  • ループ内で、別のループを実行して左から右に移動し、A より小さいものを探します
  • 小さい方を見つけたらインデックスを覚えます。今度は小さい方が数字 A になります
  • 内側のループが終了したら、開始インデックスの数値と数値 A を交換します
  • 上のループを完全に通過すると、ソートされた配列が得られます
  • アルゴリズムの実行例:

    (29, 49, 66, 35, 7, 12, 80)
    29 > 7
    (7, 49, 66, 35, 29, 12, 80)
    Round 1 ENDED
    Round 2
    (7, 49, 66, 35, 29, 12, 80)
    49 > 35
    35 > 29
    29 > 12
    (7, 12, 66, 35, 29, 49, 80)
    Round 2 ENDED
    Round 3
    (7, 12, 66, 35, 29, 49, 80)
    66 > 35
    35 > 29
    (7, 12, 29, 35, 66, 49, 80)
    Round 3 ENDED
    Round 4
    (7, 12, 29, 35, 66, 49, 80)
    (7, 12, 29, 35, 66, 49, 80)
    Round 4 ENDED
    Round 5
    (7, 12, 29, 35, 66, 49, 80)
    66 > 49
    (7, 12, 29, 35, 49, 66, 80)
    Round 5 ENDED
    Round 6
    (7, 12, 29, 35, 49, 66, 80)
    (7, 12, 29, 35, 49, 66, 80)
    Round 6 ENDED
    Sorted: (7, 12, 29, 35, 49, 66, 80)
    

    Rosetta コードで Objective-C 実装が見つかりませんでした。 、私は自分でこう書きました。

    #include <Foundation/Foundation.h>
    
    @implementation SelectionSort
    - (void)performSort:(NSMutableArray *)numbers
    {
       NSLog(@"%@", numbers);   
       for (int startIndex = 0; startIndex < numbers.count-1; startIndex++) {
          int minimalNumberIndex = startIndex;
          for (int i = startIndex + 1; i < numbers.count; i++) {
             id lhs = [numbers objectAtIndex: minimalNumberIndex];
             id rhs = [numbers objectAtIndex: i];
             if ([lhs isGreaterThan: rhs]) {
                minimalNumberIndex = i;
             }
          }
          id temporary = [numbers objectAtIndex: minimalNumberIndex];
          [numbers setObject: [numbers objectAtIndex: startIndex] 
                   atIndexedSubscript: minimalNumberIndex];
          [numbers setObject: temporary
                   atIndexedSubscript: startIndex];
       }
       NSLog(@"%@", numbers);
    }
    
    @end 

    Собрать и запустить можно либо на MacOS/Xcode, либо на любой операционной системе поддерживающей GNUstep, например у меня собирается Clang на Arch Linux.
    Скрипт сборки:

            main.m \
            -lobjc \
            `gnustep-config --objc-flags` \
            `gnustep-config --objc-libs` \
            -I /usr/include/GNUstepBase \
            -I /usr/lib/gcc/x86_64-pc-linux-gnu/12.1.0/include/ \
            -lgnustep-base \
            -o SelectionSort \

    Links

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/selectionSort

    Sources

    https://rosettacode.org/wiki/Sorting_algorithms/Selection_sort
    https://ru.wikipedia.org/wiki/Сортировка_выбором
    https://en.wikipedia.org/wiki/Selection_sort
    https://www.youtube.com/watch?v=LJ7GYbX7qpM

    カウンティングソート

    カウントソート–カウントソートアルゴリズム。に関しては?はい!そのとおりです!

    アルゴリズムには少なくとも 2 つの配列が含まれます。最初の配列は –ソートする整数のリスト、2 番目 –サイズ = (最大数 – 最小数) + 1 の配列。最初はゼロのみを含みます。次に、最初の配列から数値が並べ替えられ、その数値要素を使用して 2 番目の配列のインデックスが取得され、1 ずつ増分されます。リスト全体を調べた後、最初の配列からの数値の繰り返し数を含む、完全に満たされた 2 番目の配列が得られます。 アルゴリズムには重大なオーバーヘッドがあります – 2 番目の配列には、最初のリストにない数値のゼロも含まれます。メモリによるオーバーヘッド

    2 番目の配列を受け取った後、それを反復処理し、インデックスでソートされた数値を書き込み、カウンターを 0 にデクリメントします。最初は、ゼロ カウンタは無視されます。

    カウントソートアルゴリズムの最適化されていない操作の例:

    <オル>

  • 入力配列 1,9,1,4,6,4,4
  • カウントする配列は 0,1,2,3,4,5,6,7,8,9 になります (最小値は 0、最大値は 9)
  • 合計カウンタが 0,2,0,0,3,0,1,0,0,1 の場合
  • ソートされた配列の合計 1,1,4,4,4,6,9
  • Python 3 のアルゴリズム コード:

    
    numbers = [42, 89, 69, 777, 22, 35, 42, 69, 42, 90, 777]
    
    minimal = min(numbers)
    maximal = max(numbers)
    countListRange = maximal - minimal
    countListRange += 1
    countList = [0] * countListRange
    
    print(numbers)
    print(f"Minimal number: {minimal}")
    print(f"Maximal number: {maximal}")
    print(f"Count list size: {countListRange}")
    
    for number in numbers:
        index = number - minimal
        countList[index] += 1
    
    replacingIndex = 0
    for index, count in enumerate(countList):
        for i in range(count):
            outputNumber = minimal + index
            numbers[replacingIndex] = outputNumber
            replacingIndex += 1
    
    print(numbers)

    Из-за использования двух массивов, временная сложность алгоритма O(n + k)

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/countingSort

    Источники

    https://www.youtube.com/watch?v=6dk_csyWif0
    https://www.youtube.com/watch?v=OKd534EWcdk
    https://en.wikipedia.org/wiki/Counting_sort
    https://rosettacode.org/wiki/Sorting_algorithms/Counting_sort
    https://pro-prof.com/forums/topic/%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC-%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8-%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D0%B5%D1%82%D0%BE%D0%BC

    ボゴソート

    疑似ソートまたはスワンプ ソート。最も役に立たないソート アルゴリズムの 1 つ。

    次のように動作します。
    1. 入力は数値の配列です
    2. 数字の配列をランダムにシャッフル(シャッフル)する
    3. 配列がソートされているかどうかを確認します
    4. ソートされていない場合、配列は再度シャッフルされます
    5. 配列がランダムに並べ替えられるまで、このすべてのアクションが繰り返されます。

    ご覧のとおり、このアルゴリズムのパフォーマンスはひどいもので、賢い人は O(n * n!) さえも信じています。何年もの間、カオスの神の栄光のためにサイコロを投げることに行き詰まる可能性があります。配列は決してソートされません。それともソートされるかもしれません >

    実装

    TypeScript で実装するには、次の関数を実装する必要がありました。
    1. オブジェクトの配列をシャッフルする
    2. 配列の比較
    3. 0 から数値 (原文どおり!) までの範囲の乱数を生成します
    4. 進行状況を印刷します。並べ替えは延々と続くようです

    以下は TypeScript 実装コードです。

    const randomInteger = (maximal: number) => Math.floor(Math.random() * maximal);
    const isEqual = (lhs: any[], rhs: any[]) => lhs.every((val, index) => val === rhs[index]);
    const shuffle = (array: any[]) => {
        for (var i = 0; i < array.length; i++) { var destination = randomInteger(array.length-1); var temp = array[i]; array[i] = array[destination]; array[destination] = temp; } } let numbers: number[] = Array.from({length: 10}, ()=>randomInteger(10));
    const originalNumbers = [...numbers];
    const sortedNumbers = [...numbers].sort();
    
    let numberOfRuns = 1;
    
    do {
        if (numberOfRuns % 1000 == 0) {
            printoutProcess(originalNumbers, numbers, numberOfRuns);
        }
        shuffle(numbers);
        numberOfRuns++;
    } while (isEqual(numbers, sortedNumbers) == false)
    
    console.log(`Success!`);
    console.log(`Run number: ${numberOfRuns}`)
    console.log(`Original numbers: ${originalNumbers}`);
    console.log(`Current numbers: ${originalNumbers}`);
    console.log(`Sorted numbers: ${sortedNumbers}`);

    Для отладки можно использовать VSCode и плагин TypeScript Debugger от kakumei.

    Как долго

    Вывод работы алгоритма:

    src/bogosort.ts:1
    Still trying to sort: 5,4,8,7,5,0,2,9,7,2, current shuffle 8,7,0,2,4,7,2,5,9,5, try number: 145000
    src/bogosort.ts:2
    Still trying to sort: 5,4,8,7,5,0,2,9,7,2, current shuffle 7,5,2,4,9,8,0,5,2,7, try number: 146000
    src/bogosort.ts:2
    Still trying to sort: 5,4,8,7,5,0,2,9,7,2, current shuffle 0,2,7,4,9,5,7,5,8,2, try number: 147000
    src/bogosort.ts:2
    Still trying to sort: 5,4,8,7,5,0,2,9,7,2, current shuffle 5,9,7,8,5,4,2,7,0,2, try number: 148000
    src/bogosort.ts:2
    Success!
    src/bogosort.ts:24
    Run number: 148798
    src/bogosort.ts:25
    Original numbers: 5,4,8,7,5,0,2,9,7,2
    src/bogosort.ts:26
    Current numbers: 5,4,8,7,5,0,2,9,7,2
    src/bogosort.ts:27
    Sorted numbers: 0,2,2,4,5,5,7,7,8,9

    Для массива из 10 чисел Богосорт перемешивал исходный массив 148798 раз, многовато да?
    Алгоритм можно использовать как учебный, для понимания возможностей языка с которым предстоит работать на рынке. Лично я был удивлен узнав что в ванильных JS и TS до сих пор нет своего алгоритма перемешивания массивов, генерации целого числа в диапазоне, доступа к хэшам объектов для быстрого сравнения.

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/bogosort
    https://www.typescriptlang.org/
    https://marketplace.visualstudio.com/items?itemName=kakumei.ts-debug

    Источники

    https://www.youtube.com/watch?v=r2N3scbd_jg
    https://en.wikipedia.org/wiki/Bogosort

    GoF パターン

    Gang of Four パターンのリスト –面接で不合格になる可能性があるのと同じパターンです。

    生成パターン

    構造パターン

    行動パターン

    パターンインタープリター

    含まれるもの

    インタープリター パターンは、動作デザイン パターンを指します。このパターンを使用すると、AST ツリーを操作して独自のプログラミング言語を実装できます。AST ツリーの頂点は、言語の機能を提供する Interpret メソッドを実装する終端式と非終端式です。

    • 終端式 – 例: 文字列定数 – 「Hello World」
    • 非終端式 – たとえば、Print(“Hello World”) には、Print と終端式 “Hello World” からの引数が含まれます

    違いは何ですか?違いは、解釈は終端式で終了しますが、非終端式の場合は、入力されるすべての頂点/引数にわたって深く継続されることです。 AST ツリーが非終端式のみで構成されている場合、アプリケーションは決して完了しません。どのプロセスにも特定の有限性が必要です。この有限性が終端式の正体であり、通常、終端式には文字列などのデータが含まれます。

    AST ツリーの例を以下に示します。


    Dcoetzee、CC0、ウィキメディア コモンズ経由

    ご覧のとおり、終端式は定数と変数であり、残りは非終端式です。

    含まれないもの

    インタープリタの実装には、AST ツリーに入力された言語文字列の解析は含まれません。終端式と非終端式のクラスを実装し、入力で Context 引数を使用してメソッドを解釈し、式の AST ツリーを作成し、ルート式で Interpret メソッドを実行するだけで十分です。コンテキストを使用して、実行時にアプリケーションの状態を保存できます。

    実装

    パターンには以下が含まれます:

    • クライアント – AST ツリーを返し、ルート ノード (クライアント) に対して Interpret(context) を実行します
    • コンテキスト – アプリケーションの状態が含まれており、解釈時に式に渡されます (コンテキスト)
    • 抽象式 – Interpret(context) (Expression) メソッドを含む抽象クラス
    • ターミナル式は最終式であ​​り、抽象式 (ターミナル式) の子孫です
    • 非終端式は有限式ではありません。これには AST ツリーの深部にある頂点へのポインタが含まれます。通常、非終端式(NonterminalExpression)の解釈の結果に影響します。

    C# でのクライアントの例

            static void Main(string[] args)
            {
                var context = new Context();
                var initialProgram = new PerformExpression(
                    new IExpression[] {
                        new SetExpression("alpha", "1"),
                        new GetExpression("alpha"),
                        new PrintExpression(
                            new IExpression[] {
                                new ConstantExpression("Hello Interpreter Pattern")
                            }
                        )
                    }
                );
                System.Console.WriteLine(initialProgram.interpret(context));
            }
    }
    

    C# での抽象式の例

    {
        String interpret(Context context);
    }
    

    C# のターミナル式の例 (文字列定数)

    {
        private String constant;
    
        public ConstantExpression(String constant) {
            this.constant = constant;
        }
    
        override public String interpret(Context context) {
            return constant;
        }
    }
    

    C# の非終端式の例 (区切り文字「;」を使用して、下位頂点の結果を開始して連結する)

    {
        public PerformExpression(IExpression[] leafs) : base(leafs) {
            this.leafs = leafs;
        }
        
        override public String interpret(Context context) {
            var output = "";
            foreach (var leaf in leafs) {
                output += leaf.interpret(context) + ";";
            }
            return output;
        }
    }
    

    機能的に実行できますか?

    知られているように、すべてのチューリング完全言語は同等です。オブジェクト指向パターンを関数型プログラミング言語に移植することは可能ですか?

    実験として、Elm という Web 用の FP 言語を取り上げてみましょう。 Elm にはクラスはありませんが、レコードとタイプがあるため、次のレコードとタイプが実装に関係します。

    • 式 – 考えられるすべての言語式のリスト (式)
    • 従属式 – 非終端式 (ExpressionLeaf) に従属する式
    • コンテキスト – アプリケーションの状態 (コンテキスト) を保存するレコード
    • Interpret(context) メソッドを実装する関数 – ターミナル式と非ターミナル式の機能を実装する必要なすべての関数
    • インタープリターの状態の補助レコード – インタープリターの正しい動作に必要で、インタープリターの状態とコンテキストを保存します

    Elm で使用可能な式のセット全体の解釈を実装する関数の例:

      case input.expression of
        Constant text ->
          { 
            output = text, 
            context = input.context 
          }
        Perform leafs ->
          let inputs = List.map (\leaf -> { expressionLeaf = leaf, context = input.context } ) leafs in
            let startLeaf = { expressionLeaf = (Node (Constant "")), context = { variables = Dict.empty } } in
              let outputExpressionInput = List.foldl mergeContextsAndRunLeafs startLeaf inputs in
                {
                  output = (runExpressionLeaf outputExpressionInput).output,
                  context = input.context
                }
        Print printExpression ->
          run 
          { 
            expression = printExpression, 
            context = input.context 
          }
        Set key value ->
          let variables = Dict.insert key value input.context.variables in
          {
            output = "OK",
            context = { variables = variables }
          }
        Get key ->
          {
            output = Maybe.withDefault ("No value for key: " ++ key) (Dict.get key input.context.variables),
            context = input.context
          }
    

    解析についてはどうですか?

    ソース コードを AST ツリーに解析することはインタープリター パターンには含まれていません。ソース コードを解析するにはいくつかの方法がありますが、それについてはまた別の機会に説明します。
    Elm のインタープリタの実装では、頂点の解析と下位頂点の解析という 2 つの関数で構成される単純なパーサーを AST ツリーに作成しました。

    parseLeafs state =
        let tokensQueue = state.tokensQueue in
            let popped = pop state.tokensQueue in
                let tokensQueueTail = tail state.tokensQueue in
                    if popped == "Nothing" then
                        state
                    else if popped == "Perform(" then
                        {
                            tokensQueue = tokensQueue,
                            result = (state.result ++ [Node (parse tokensQueue)])
                        }
                    else if popped == ")" then
                        parseLeafs {
                            tokensQueue = tokensQueueTail,
                            result = state.result
                        }
                    else if popped == "Set" then
                        let key = pop tokensQueueTail in
                            let value = pop (tail tokensQueueTail) in
                                parseLeafs {
                                    tokensQueue = tail (tail tokensQueueTail),
                                    result = (state.result ++ [Node (Set key value)])
                                }
                    else if popped == "Get" then
                        let key = pop tokensQueueTail in
                            parseLeafs {
                                tokensQueue = tail tokensQueueTail,
                                result = (state.result ++ [Node (Get key)])
                            }
                    else 
                        parseLeafs {
                            tokensQueue = tokensQueueTail,
                            result = (state.result ++ [Node (Constant popped)])
                        }
    
    parse tokensQueue =
        let popped = pop tokensQueue in
            let tokensQueueTail = tail tokensQueue in
                if popped == "Perform(" then
                    Perform (
                        parseLeafs {
                            tokensQueue = tokensQueueTail, 
                            result = []
                        }
                    ).result
                else if popped == "Set" then
                    let key = pop tokensQueueTail in
                        let value = pop (tail tokensQueueTail) in
                            Set key value
                else if popped == "Print" then
                    Print (parse tokensQueueTail)
                else
                    Constant popped
    

    リンク

    https://gitlab.com/demensdeum /patterns/-/tree/master/interpreter/elm
    https://gitlab.com/demensdeum/patterns/-/tree/master/interpreter/csharp

    ソース

    https://en.wikipedia.org/wiki/Interpreter_pattern
    https://elm-lang.org/
    https://docs.microsoft.com/en-us/dotnet/csharp/

    RGB画像をグレーに

    この投稿では、RGB バッファをグレー (グレースケール) に変換するアルゴリズムについて説明します。
    これは非常に簡単に行われ、バッファの各ピクセル カラー チャネルが特定の式に従って変換され、出力はグレーの画像になります。
    平均的な方法:

    red = average;
    green = average;
    blue = average;

    Складываем 3 цветовых канала и делим на 3.

    Однако существует еще один метод – метод средневзвешенный, он учитывает цветовосприятие человека:

    red = luminance;
    green = luminance;
    blue = luminance;

    Какой метод лучше использовать? Да какой вам больше подходит для конкретной задачи. Далее сравнение методов с помощью тестовой цветовой сетки:

    Пример реализации на JavaScript + HTML 5

        image,
        canvas,
        weightedAverage
    ) {
        const context = canvas.getContext('2d');
    
        const imageWeight = image.width;
        const imageHeight = image.height;
    
        canvas.width = imageWeight;
        canvas.height = imageHeight;
    
        context.drawImage(image, 0, 0);
    
        let pixels = context
            .getImageData(
                0,
                0,
                imageWeight,
                imageHeight
            );
    
        for (let y = 0; y & lt; pixels.height; y++) {
            for (let x = 0; x & lt; pixels.width; x++) {
                const i = (y * 4) * pixels.width + x * 4;
    
                let red = pixels.data[i];
                let green = pixels.data[i + 1];
                let blue = pixels.data[i + 2]
    
                const average = (red + green + blue) / 3;
                const luminance = 0.2126 * red +
                    0.7152 * green +
                    0.0722 * blue;
    
                red = weightedAverage ? luminance : average;
                green = weightedAverage ? luminance : average;
                blue = weightedAverage ? luminance : average;
    
                pixels.data[i] = red;
                pixels.data[i + 1] = green;
                pixels.data[i + 2] = blue;
            }
        }
        context
            .putImageData(
                pixels,
                0,
                0,
                0,
                0,
                pixels.width,
                pixels.height
            );
    }

    Источники

    https://www.baeldung.com/cs/convert-rgb-to-grayscale
    https://twitter.com/mudasobwa/status/1528046455587495940
    https://rosettacode.org/wiki/Grayscale_image

    Ссылки

    http://papugi.demensdeum.repl.co/

    Благодарности

    Спасибо Aleksei Matiushkin (https://twitter.com/mudasobwa) за наводку на Rosetta Code

    Macbook M1 で CSGO を実行する方法

    Macbook M1 で CSGO の起動時に SDL_GetDesktopDisplayMode_REAL エラーが発生した場合は、以下の手順を実行してください。
    1. CSGO の起動オプションを Steam に追加します。
    <コード>-w 1440 -h 900 -全画面
    2.Steam経由でCSGOを起動
    3. エラー SDL_GetDesktopDisplayMode_REAL
    を [無視] または [常に無視] をクリックします。4. 楽しんでください