Wi-FiルーターとしてのRaspberry PI 3

Raspberry Pi (RPI) から Wi-Fi ルーターを作成する方法については、インターネット上にたくさんの記事があります。この記事では、Sing-Box を搭載した Wi-Fi ルーターを作成する方法を簡単に説明します。ここで説明した方法は現時点では機能しますが、将来的には大きく変更される可能性があります。したがって、このメモは、これから直面する問題の大まかな概要として使用してください。

SSH

OpenWrt の操作方法がわからない人には、dietPI をインストールすることをお勧めします。
RPI を eth0 経由で現在のルーターに接続し、そこに SSH 経由で接続します。 RPI IP アドレスは、ルーターの dhcp パネルで確認できます。たとえば次のように root に直接接続します。
<コード>
ssh root@[IP_ADDRESS]

Wi-Fi アダプター

内蔵のRPI3は正直弱く、5GHzをサポートしていないことが判明しました。したがって、USB 2 経由で Realtek RTL8811CU チップセット上の RITMIX RWA-150 アダプターを接続しました。ドライバーは、私の DietPi バージョンの Linux カーネルに追加されました。次に、dietpi-configを使用して、内蔵Wi-Fiを完全にオフにしました。その結果、wlan0 USB アダプターは 1 つだけ残りました。

アクセスポイント

root のデフォルトの DietPI パスワードは Dietpi です。接続すると、dietPI インストーラー/コンフィギュレーターが表示されます。完了したら、デバイスが再起動するため、再度接続する必要があります。

まず、デバイスがアクセス ポイントを認識できるように hostapd を構成する必要があります。 hostapd がインストールされていない場合は、apk 経由でインストールします。

次に、hostapd の構成を記述する必要があります。私の構成の例:

interface=wlan0
driver=nl80211
ssid=MyPiAP
hw_mode=a
channel=157
wmm_enabled=1

auth_algs=1
wpa=2
wpa_passphrase=your_password
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
ieee80211n=1
ieee80211ac=0
ieee80211ax=0
country_code=RU

hostapd 設定の意味についてはマニュアルを参照してください。ただし、重要なのは、チャネル (2.4 GHz または 5 GHz)、国コードを自分で設定することです。これがないと、ローカライズされたデバイスがアクセス ポイントで正しく動作できません。私はすでにこれを行っており、知っているので、国を慎重に設定してください。

DHCP

次に、dnsmasq をインストールして構成し、DHCP を実装します。これは、接続されたコンピュータが IP アドレスと DNS サーバーを決定するために必要です。
私の構成の例:

interface=wlan0
dhcp-range=172.19.0.10,172.19.0.200,255.255.255.0,12h

dhcp-option=3,172.19.0.1

dhcp-option=6,1.1.1.1,8.8.8.8

no-resolv

server=1.1.1.1
server=8.8.8.8

これは、アクセス ポイントに接続して IP アドレスを取得できるようにするための最小限の構成です。次に、ルーティングと NAT を構成する必要があります。これは、接続されたコンピュータがインターネットにアクセスできるようにするために必要です。

ここでのメモは、インターネット上に多くの記事がある、通常の Debian 互換システムでの典型的なルーティング設定のカテゴリに属します。その後、すべては追求する目標によって異なります。たとえば、システム内の新しいインターフェイスとして外部サーバーに接続するか、単に wlan0 <-> eth0 を実行するか、これで RPI の詳細は終了し、好みに合わせて設定します。

また、systemctl を介してカスタム システム サービスを構成する必要性についても触れておきたいと思います。サービスをチェーンで接続する必要がある場合があります。これはすべて、ネットワーク上の systemctl マニュアルに記載されています。サービス レベルで問題がある場合は、journalctl のログを確認してください。

結論

速度測定から、Wi-Fi 経由で RPI3 から約 50Mbps を引き出すことができました (5GHz アダプターの接続後)。これは、ルーターに直接接続した場合と比較して速度が半分低下することを意味します。生産性の高い RPI モデルを使用すると、より良い結果が得られることは認めますが、専用の OpenWrt デバイスや既製のソリューションがニーズに適している可能性もあります。

ソース

https://forums.raspberrypi.com/viewtopic.php?t=394710
https://superuser.com/questions/1408586/raspberry-pi-wifi-hotspot-slow-internet-speed
https://www.youtube.com/watch?v=jlHWnKVpygw

ローカル画像生成: ComfyUI および FLUX モデル

現在では、クラウド サービスに依存する必要はなく、独自のハードウェアだけで高品質の画像を生成できます。この投稿では、ComfyUI を使用して最新の FLUX モデルをコンピューター上でローカルに実行する方法について説明します。

ComfyUI はノードベースのアーキテクチャを使用します。これにより、次のことが可能になります。
– 生成のあらゆる段階を完全に制御します。
– 既成の「ワークフロー」を簡単に共有

FLUX は大規模なモデルであるため、ハードウェア要件は SD 1.5 または SDXL よりも高くなります。
ビデオ カード (GPU): 12 GB VRAM 以上を搭載した Nvidia RTX (快適な作業用)。 8 GB 以下の場合は、量子化バージョン (GGUF または NF4) を使用する必要があります。
ランダム アクセス メモリ (RAM): 最低 16 GB (32 GB 以上が望ましい)。
ディスク容量: モデルとコンポーネント用に約 20 ~ 50 GB。

FLUX を開始する最も簡単な方法は、既製のテンプレートを使用することです。ワークフロー ウィンドウで Flux Text to image を検索してインストールするだけです。

「Text to Image (Flux.1 Dev)」ノードに英語でプロンプトを書き込み、解像度 (FLUX は 1024×1024 以上で適切に動作します) を選択して、実行 を押します。

第 1 世代では、モデルがビデオ カード メモリにロードされるため、時間がかかる場合があります。

https://github.com/comfyanonymous/ComfyUI

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 つです。

    オラマコール

    Ollama を使用していて、毎回独自の API ラッパーを作成したくない場合は、
    ollama_call プロジェクトにより、作業が大幅に簡素化されます。

    これは、1 つの関数でローカル LLM にリクエストを送信できる小さな Python ライブラリです。
    JSON 形式などの応答をすぐに受け取ります。

    インストール

    pip install ollama-call
    

    なぜ必要なのか

    • モデルを操作するための最小限のコード;
    • さらなる処理のための構造化された JSON レスポンス;
    • ラピッドプロトタイプや MVP に便利です。
    • 必要に応じてストリーミング出力をサポートします。

    使用例

    from ollama_call import ollama_call
    
    response = ollama_call(
        user_prompt="Hello, how are you?",
        format="json",
        model="gemma3:12b"
    )
    
    print(response)
    

    特に役立つ場合

    • Ollama 上でスクリプトまたはサービスを作成します。
    • 予測可能な応答形式が必要です。
    • 重いフレームワークを接続する必要はありません。

    合計

    ollama_call は、Python から Ollama を操作するための軽量で明確なラッパーです。
    シンプルさと迅速な結果が重要な場合に適した選択です。

    GitHub
    https://github.com/demensdeum/ollama_call

    SFAP: 最新のデータ取得と処理のためのモジュール式フレームワーク

    自動化と人工知能の積極的な開発の文脈において、効果的に収集するタスクは、
    データのクリーニングと変換が重要になります。ほとんどのソリューションは閉じるだけです
    このプロセスの別々の段階では、複雑な統合とサポートが必要になります。

    SFAP (シーク・フィルター・適応・公開) は、Python のオープンソース プロジェクトです。
    これは、ライフサイクルのすべての段階でデータを処理するための総合的かつ拡張可能なアプローチを提供します。
    ソースの検索から完成した結果の公開まで。

    SFAP とは

    SFAP は、データ処理パイプラインの明確な概念に基づいて構築された非同期フレームワークです。
    各ステージは論理的に分離されており、独立して拡張または置換できます。

    このプロジェクトは責任連鎖アーキテクチャ パターンに基づいており、次の機能が提供されます。

    • パイプライン構成の柔軟性
    • 個々のステージの簡単なテスト
    • 高負荷に対するスケーラビリティ
    • コンポーネント間の責任を明確に分離する

    パイプラインの主なステージ

    シーク – データ検索

    この段階では、Web ページ、API、ファイル ストレージなどのデータ ソースが検出されます。
    または他の情報の流れ。 SFAP により、変更せずに新しいソースに簡単に接続できます
    システムの残りの部分。

    フィルタ – フィルタリング

    フィルタリングは、無関係なコンテンツ、重複、技術的要素などのノイズを除去するように設計されています。
    そして低品質のデータ。これは後続の処理ステップにとって重要です。

    適応 – 適応と処理

    適応ステージは、正規化、構造化、データ変換などのデータ変換を担当します。
    セマンティック処理と AI モデル (生成モデルを含む) との統合。

    発行 – 出版

    最終段階では、データはデータベース、API、ファイル、外部サービスなどのターゲット形式で公開されます。
    またはコンテンツプラットフォーム。 SFAP は、結果の配信方法を制限しません。

    プロジェクトの主な特徴

    • asyncio に基づく非同期アーキテクチャ
    • モジュール性と拡張性
    • 複雑な処理パイプラインのサポート
    • AI/LLM ソリューションとの統合の準備ができている
    • 高負荷のシステムに適しています

    実際の使用例

    • ニュースソースの集約と分析
    • 機械学習用のデータセットを準備する
    • 自動化されたコンテンツ パイプライン
    • 大規模なデータ ストリームのクレンジングと正規化
    • 異種ソースからのデータの統合

    SFAP を使ってみる

    始めるために必要なのは次のとおりです。

    <オル>

  • プロジェクト リポジトリのクローンを作成します。
  • Python の依存関係をインストールします。
  • 独自のパイプライン ステップを定義します。
  • 非同期データ処理プロセスを開始します。
  • プロジェクトは特定のビジネス タスクに簡単に適応でき、システムとともに成長することができます。
    一枚岩にならずに。

    結論

    SFAP は単なるパーサーやデータ コレクターではなく、構築のための本格的なフレームワークです。
    最新のデータ パイプライン システム。を重視する開発者やチームに適しています。
    スケーラブルで、アーキテクチャ的にクリーンで、データ対応が可能です。
    プロジェクトのソース コードは GitHub で入手できます。
    https://github.com/demensdeum/SFAP

    なぜバグを修正できないのですか?

    コードの作業に何時間も費やし、仮説を検討し、条件を調整しても、バグは依然として再現されます。おなじみですね?この欲求不満の状態は「ゴーストハンティング」と呼ばれることがあります。プログラムは、あなたの修正を無視して、独自の生活を送っているようです。

    この状況の最も一般的な、そして最も迷惑な理由の 1 つは、アプリケーション内のまったく間違った場所でエラーを探していることです。

    「偽症状」の罠

    エラーを見つけると、エラーが「発生」した場所に注意が集まります。しかし、複雑なシステムでは、バグ (クラッシュまたは不正な値) が発生しても、それは長い一連のイベントの終わりにすぎません。結末を修正しようとすると、病気ではなく症状と闘うことになります。

    ここでフローチャートの概念が登場します。

    実際にどのように機能するか

    もちろん毎回紙にフローチャートを直接書く(描画する) 必要はありませんが、アーキテクチャのガイドとして頭の中に入れておくか、手元に置いておくことが重要です。フローチャートを使用すると、アプリケーションの操作を結果のツリーとして視覚化できます。

    この構造を理解していないと、開発者はしばしば暗中模索になります。状況を想像してみてください。1 つの条件ブランチでロジックを編集している間に、アプリケーションは (特定のパラメータのセットにより) 考えもしなかったまったく別のブランチに移動します。

    <ブロック引用>
    結果: アルゴリズムの一部で「完璧な」コード修正に何時間も費やしましたが、当然のことながら、アルゴリズムの別の部分で実際に失敗した問題は何も修正されません。


    バグを克服するためのアルゴリズム

    閉ざされたドアを叩くのをやめるには、診断のアプローチを変える必要があります。

    • 結果ツリーで状態を見つける:コードを記述する前に、アプリケーションがたどったパスを正確に判断する必要があります。どの時点で論理が間違った方向に進んだのでしょうか?問題の原因となった特定の状態 (状態) は何ですか?
    • 再現は成功の 80% です: これは通常、テスターと自動テストによって行われます。バグが「浮遊」している場合、開発は条件を共同で検索するプロセスに関与します。
    • できるだけ多くの情報を使用する: ローカリゼーションには、ログ、OS バージョン、デバイス パラメータ、接続タイプ (Wi-Fi/5G)、さらには特定の通信事業者さえも重要です。

    エラーの瞬間を捉えた「写真」

    理想的には、バグを修正するには、バグが再現された時点のアプリケーションの完全な状態を取得する必要があります。インタラクション ログも非常に重要です。インタラクション ログには、最終ポイントだけでなく、ユーザー パス全体 (障害が発生する前にどのようなアクションが行われたか) も示されます。これは、同様の状態を再度作成する方法を理解するのに役立ちます。

    今後のヒント: 複雑なケースが発生した場合は、その状況が再び発生した場合に備えて、コードのこのセクションに拡張デバッグ ログ情報を追加してください。


    AI 時代の「とらえどころのない」状態の問題

    LLM (大規模言語モデル) を使用する最新のシステムでは、古典的な決定論 (「1 つの入力、1 つの出力」) が違反されることがよくあります。まったく同じ入力データを渡しても、異なる結果が得られます。

    これは現代の運用システムの非決定性が原因で発生します。

    • GPU 並列処理: GPU 浮動小数点演算は常に結合的であるとは限りません。スレッドの並列実行により、数値の加算順序が若干変更される可能性があり、結果に影響を与える可能性があります。
    • GPU の温度とスロットル: 実行速度と負荷分散は、ハードウェアの物理的状態に依存する場合があります。大規模なモデルでは、こうした微細な違いが蓄積され、出力時に異なるトークンが選択される可能性があります。
    • 動的バッチ処理: クラウドでは、リクエストが他のリクエストと結合されます。バッチサイズが異なると、カーネルでの計算の計算が変わります。

    このような状況では、「同じ状態」を再現することはほぼ不可能になります。ここであなたを救うことができるのは、テストへの統計的アプローチのみです。


    ロジックが失敗した場合: メモリの問題

    「安全でない」言語 (C または C++) を使用している場合、メモリ破損が原因でバグが発生する可能性があります。

    これらは最も深刻なケースです。1 つのモジュールでエラーが発生すると、別のモジュールのデータが「上書き」される可能性があります。これにより、通常のアプリケーション ロジックを使用して追跡できない、完全に説明不可能な孤立した障害が発生します。

    アーキテクチャ レベルで身を守るにはどうすればよいですか?

    このような「謎の」バグを回避するには、最新のアプローチを使用する必要があります。

    • マルチスレッド プログラミング パターン:明確な同期により競合状態が排除されます。
    • スレッドセーフな言語: コンパイル時にメモリの安全性を保証するツール:
      • Rust: 所有権システムによりメモリ エラーが排除されます。
      • Swift 6 同時実行性:強力なデータ分離チェック。
      • Erlang: アクター モデルを通じてプロセスを完全に分離します。

    概要

    バグを修正するということは、新しいコードを書くことではなく、古いコードがどのように機能するかを理解することです。管理者が触れてもいないブランチを編集するのは時間を無駄にする可能性があることを覚えておいてください。システムの状態を記録し、AI の非決定性の要素を考慮して、安全なツールを選択します。

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

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

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

    例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への完全な依存性

    主な理由は、多くの開発者がLLMに完全に依存しており、彼らが働いているツールを深く理解する必要性を無視していることです。ドキュメントを勉強する代わりに、チャットリクエスト。エラーの理由を分析する代わりに、決定をコピーします。建築ソリューションの代わりに、説明に従ってコンポーネントの生成。これはすべて表面的なレベルで機能しますが、非標準タスクが発生するとすぐに、実際のプロジェクトとの統合または微調整の必要性が必要であり、すべてが崩れます。

    コンテキストと時代遅れの慣行の欠如

    ニューラルネットワークは、一般化されたコードを生成します。彼らは、あなたのプラットフォームの詳細、図書館のバージョン、環境制限、またはプロジェクトの建築ソリューションを考慮していません。生成されるものはしばしばもっともらしいように見えますが、実際のサポートされているコードとは何の関係もありません。それらが時代遅れのバージョンのフレームワークに属している場合、または長い間効果のないまたは安全でないと認識されてきたアプローチを使用する場合、単純な推奨事項でさえも機能しない場合があります。モデルはコンテキストを理解していません – 統計に依存しています。これは、オープンコードで人気のあるエラーとアンチパットターンが何度も何度も再現されることを意味します。

    冗長性、非効率性、プロファイリングの欠如

    生成されたAIを生成するコードは、多くの場合冗長です。不要な依存関係、ロジックの複製、抽象化を不必要に追加します。サポートが困難な効果的で重い構造が判明しました。これは、ギャングのサイズ、応答時間、エネルギー消費が重要であるモバイル開発では特に深刻です。

    ニューラルネットワークはプロファイリングを実施せず、CPUとGPUの制限を考慮していないため、メモリの漏れを気にしません。コードが実際にどれほど効果的であるかを分析しません。最適化は依然として手作りであり、分析と検査が必要です。それがなければ、アプリケーションは、構造の観点から「正しい」ように見える場合でも、遅く、不安定でリソース集中的になります。

    脆弱性とセキュリティに対する脅威

    安全を忘れないでください。 LLMを使用して部分的または完全に作成されたプロジェクトが正常にハッキングされた場合、すでに既知のケースがあります。理由は典型的なものです。危険な関数の使用、入力データの検証の欠如、承認の論理のエラー、外部依存関係の漏れです。ニューラルネットワークは、オープンリポジトリで見つかったという理由だけで、脆弱なコードを生成できます。セキュリティスペシャリストの参加と完全な改訂がなければ、このようなエラーは攻撃の入力ポイントに簡単になります。

    法律はパレートであり、欠陥の本質

    パレートの法律は、ニューラルネットワークではっきりと機能します。努力の20%のために結果の80%が達成されます。このモデルは、大量のコードを生成し、プロジェクトの基礎を作成し、構造を広げ、タイプをアレンジし、モジュールを接続できます。ただし、これはすべて時代遅れであり、現在のバージョンのライブラリまたはフレームワークと互換性があり、重要な手動改訂が必要です。ここでの自動化は、プロジェクトの特定の現実にチェックされ、処理され、適応する必要があるドラフトとして機能します。

    注意楽観主義

    それにもかかわらず、未来は励みに見えます。トレーニングデータセットの絶え間ない更新、現在のドキュメントとの統合、自動化されたアーキテクチャチェック、設計およびセキュリティパターンへのコンプライアンス – これがすべてゲームのルールを根本的に変更する可能性があります。おそらく数年後には、コードをより速く、より安全で、より効率的に書くことができ、LLMに真の技術的な共著者として依存することができます。しかし、今のところ – 悲しいかな – 多くのことをチェックし、書き直し、手動で修正する必要があります。

    ニューラルネットワークは強力なツールです。しかし、彼があなたに反対するのではなく、あなたのために働くためには、いつでもコントロールするための基盤、批判的思考、意欲が必要です。

    バイブコアトリック:なぜ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

    Flash Forever – Interceptor 2021

    Recently, it turned out that Adobe Flash works quite stably under Wine. During a 4-hour stream, I made the game Interceptor 2021, which is a sequel to the game Interceptor 2020, written for the ZX Spectrum.

    For those who are not in the know – the Flash technology provided interactivity on the web from 2000 to around 2015. Its shutdown was prompted by an open letter from Steve Jobs, in which he wrote that Flash should be consigned to history because it lagged on the iPhone. Since then, JS has become even more sluggish than Flash, and Flash itself has been wrapped in JS, making it possible to run it on anything thanks to the Ruffle player.

    You can play it here:
    https://demensdeum.com/demos/Interceptor2021

    Video:
    https://www.youtube.com/watch?v=-3b5PkBvHQk

    Source code:
    https://github.com/demensdeum/Interceptor-2021

    CRUDリポジトリ

    このノートでは、よく知られている古典的な CRUD パターン、Swift 言語での実装の基本原則について説明します。 Swift は、Windows、Linux、macOS、iOS、Android で利用できるオープンなクロスプラットフォーム言語です。

    データ ストレージとアプリケーション ロジックを抽象化するためのソリューションは数多くあります。そのようなソリューションの 1 つが CRUD アプローチです。これは C– の頭字語です。作成、R -読み取り、U –最新情報、D–削除
    します。通常、この原則はデータベースへのインターフェイスの実装を通じて実装され、要素は id などの一意の識別子を使用して操作されます。 CRUD – 文字ごとにインターフェイスが作成されます。作成(オブジェクト、ID)、読み取り(ID)、更新(オブジェクト、ID)、削除(オブジェクト、ID)。
    オブジェクトの内部に ID が含まれている場合、オブジェクト全体がフィールド – とともに渡されるため、メソッド (Create、Update、Delete) から id 引数を省略できます。 ID。しかし、– についてはID によってデータベースからオブジェクトを取得したいため、読み取りには ID が必要です。

    名前はすべて架空のものです

    仮想の AssistantAI アプリケーションが無料の EtherRelm データベース SDK を使用して作成され、統合は簡単で、API は非常に便利で、その結果、アプリケーションが市場にリリースされたと想像してみましょう。
    突然、SDK 開発者の EtherRelm は有料化を決定し、アプリケーション ユーザーあたりの価格を年間 100 ドルに設定しました。
    何?はい! AssistantAI の開発者は、すでに 100 万人のアクティブ ユーザーを抱えているため、今何をすべきでしょうか。 1億ドル支払う?
    代わりに、プラットフォーム固有の RootData データベースへのアプリケーションの転送を評価する決定が行われます。プログラマーによると、そのような転送には約 6 か月かかりますが、これにはアプリケーションの新機能の実装は考慮されていません。いくつか考えた結果、アプリケーションを市場から削除し、BueMS データベースが組み込まれた別の無料のクロスプラットフォーム フレームワークに書き直すことが決定されました。これにより、有料データベースの問題が解決され、他のプラットフォームでの開発が簡素化されます。
    1 年後、アプリケーションは BueMS で書き直されましたが、突然フレームワーク開発者が有料化を決定しました。チームは同じ罠に 2 度陥ったことが判明しました。2 度目から抜け出せるかどうかはまったく別の話です。

    抽象化が助けになります

    開発者がアプリケーション内でインターフェイスの抽象化を使用していれば、これらの問題は回避できたはずです。 OOPの3つの柱へ–ポリモーフィズム、カプセル化、継承、少し前に別の – が追加されました。抽象化
    します。データ抽象化により、ビジネス上の問題を解決するために使用される特定の実装を実装するのに十分な正確さを保ちながら、アイデアやモデルを最小限の詳細で一般的な用語で記述することができます。
    アプリケーションロジックが依存しないようにデータベース操作を抽象化するにはどうすればよいでしょうか?私たちは CRUD アプローチを使用しています!

    簡略化された UML CRUD 図は次のようになります。

    架空の EtherRelm データベースの例:

    実際の SQLite データベースの例:

    すでにお気づきのとおり、データベースを切り替えると、アプリケーションがやり取りする CRUD インターフェイスのみが変更され、変更されません。 CRUD は GoF パターンの実装です。アダプターなので、これを使用して、アプリケーション インターフェイスをあらゆるデータベースに適合させ、互換性のないインターフェイスを結合します。
    言葉は空です、コードを見せてください
    プログラミング言語で抽象化を実装するには、インターフェイス/プロトコル/抽象クラスが使用されます。これらはすべて同じ程度の現象ですが、インタビュー中にそれらの違いを挙げるように求められることがありますが、個人的にはこれはあまり意味がないと考えています。使用の唯一の目的はデータ抽象化を実装することです。それ以外の場合は、インタビュー対象者の記憶をテストすることです。
    CRUD は多くの場合、リポジトリ パターンのフレームワーク内で実装されますが、リポジトリは CRUD インターフェイスを実装する場合と実装しない場合があり、すべて開発者の創意工夫に依存します。

    UserDefaults データベースを直接操作する、Book 構造リポジトリの非常に典型的な Swift コードを考えてみましょう。

    
    struct Book: Codable {
    	let title: String
    	let author: String
    }
    
    class BookRepository {
    	func save(book: Book) {
        		let record = try! JSONEncoder().encode(book)
        		UserDefaults.standard.set(record, forKey: book.title)
    	}
        
    	func get(bookWithTitle title: String) -> Book? {
        		guard let data = UserDefaults.standard.data(forKey: title) else { return nil }
        		let book = try! JSONDecoder().decode(Book.self, from: data)
        		return book
    	}
        
    	func delete(book: Book) {
        		UserDefaults.standard.removeObject(forKey: book.title)
    	}
    }
    
    let book = Book(title: "Fear and Loathing in COBOL", author: "Sir Edsger ZX Spectrum")
    let repository = BookRepository()
    repository.save(book: book)
    print(repository.get(bookWithTitle: book.title)!)
    repository.delete(book: book)
    guard repository.get(bookWithTitle: book.title) == nil else {
    	print("Error: can't delete Book from repository!")
    	exit(1)
    }
    

    上記のコードは単純に見えますが、DRY (Do notrepeat Yourself) 原則の違反の数とコードの一貫性を数えてみましょう。
    UserDefaults データベースへの接続
    JSON エンコーダーおよびデコーダーとの接続 – #8211; JSONエンコーダ、JSONデコーダ
    Book 構造に接続されていますが、データベースに保存する構造ごとにリポジトリ クラスを作成しないように抽象リポジトリが必要です (DRY 違反)

    この種の CRUD リポジトリ コードはよく見かけます。使用することはできますが、コードの高度な結合と重複により、時間の経過とともにそのサポートが非常に複雑になるという事実につながります。これは、別のデータベースに切り替えようとする場合、またはアプリケーションで作成されたすべてのリポジトリでデータベースを操作する内部ロジックを変更する場合に特に顕著になります。
    コードを複製する代わりに、カップリングを高く保ちます。 CRUD リポジトリのプロトコルを作成して、データベース インターフェイスとアプリケーション ビジネス ロジックを抽象化し、DRY を尊重し、低結合を実装しましょう。

        typealias Item = Codable
        typealias ItemIdentifier = String
        
        func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws
        func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T
        func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws
        func delete(id: CRUDRepository.ItemIdentifier) async throws
    }
    

    CRUDRepository プロトコルは、特定の CRUD リポジトリをさらに実装するためのインターフェイスと関連するデータ型を記述します。

    次に、UserDefaults データベースの特定の実装を作成します。

        private typealias RecordIdentifier = String
        
        let tableName: String
        let dataTransformer: DataTransformer
        
        init(
       	 tableName: String = "",
       	 dataTransformer: DataTransformer = JSONDataTransformer()
        ) {
       	 self.tableName = tableName
       	 self.dataTransformer = dataTransformer
        }
        
        private func key(id: CRUDRepository.ItemIdentifier) -> RecordIdentifier {
       	 "database_\(tableName)_item_\(id)"
        }
       	 
        private func isExists(id: CRUDRepository.ItemIdentifier) async throws -> Bool {
       	 UserDefaults.standard.data(forKey: key(id: id)) != nil
        }
        
        func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
       	 let data = try await dataTransformer.encode(item)
       	 UserDefaults.standard.set(data, forKey: key(id: id))
       	 UserDefaults.standard.synchronize()
        }
        
        func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T {
       	 guard let data = UserDefaults.standard.data(forKey: key(id: id)) else {
       		 throw CRUDRepositoryError.recordNotFound(id: id)
       	 }
       	 let item: T = try await dataTransformer.decode(data: data)
       	 return item
        }
        
        func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
       	 guard try await isExists(id: id) else {
       		 throw CRUDRepositoryError.recordNotFound(id: id)
       	 }
       	 let data = try await dataTransformer.encode(item)
       	 UserDefaults.standard.set(data, forKey: key(id: id))
       	 UserDefaults.standard.synchronize()
        }
        
        func delete(id: CRUDRepository.ItemIdentifier) async throws {
       	 guard try await isExists(id: id) else {
       		 throw CRUDRepositoryError.recordNotFound(id: id)
       	 }
       	 UserDefaults.standard.removeObject(forKey: key(id: id))
       	 UserDefaults.standard.synchronize()
        }
    }
    

    コードは長く見えますが、疎結合を含む CRUD リポジトリの完全な具体的な実装が含まれています。詳細は以下で説明します。
    コードの自己文書化のために typealias が追加されました。
    弱い結合と強い結合
    について。特定の構造 (struct) からの切り離しは、汎用 T を使用して実装されます。これには、Codable プロトコルを実装する必要があります。 Codable では、TopLevelEncoder および TopLevelDecoder プロトコルを実装するクラス (JSONEncoder や JSONDecoder など) を使用して構造を変換できます。基本型 (Int、String、Float など) を使用する場合、構造を変換するために追加のコードを記述する必要はありません。 p>

    特定のエンコーダとデコーダからの分離は、DataTransformer プロトコルの抽象化を使用して行われます。

    	func encode<T: Encodable>(_ object: T) async throws -> Data
    	func decode<T: Decodable>(data: Data) async throws -> T
    }
    

    データ トランスフォーマーの実装を使用して、エンコーダーとデコーダーのインターフェイスの抽象化を実装し、さまざまなタイプのデータ形式で確実に動作するように疎結合を実装しました。

    以下は、特定の DataTransformer、つまり JSON のコードです。

    	func encode<T>(_ object: T) async throws -> Data where T : Encodable {
        		let data = try JSONEncoder().encode(object)
        		return data
    	}
        
    	func decode<T>(data: Data) async throws -> T where T : Decodable {
        		let item: T = try JSONDecoder().decode(T.self, from: data)
        		return item
    	}
    }
    

    それは可能でしたか?

    何が変わりましたか?今後は、Codable プロトコルを実装する構造を操作できるように特定のリポジトリを初期化するだけで十分です。そのため、コードを複製する必要がなく、アプリケーションの疎結合を実装できます。

    特定のリポジトリを使用したクライアント CRUD の例。UserDefaults はデータベース、JSON データ形式、クライアント構造であり、配列の書き込みと読み取りの例でもあります。

    
    print("One item access example")
    
    do {
    	let clientRecordIdentifier = "client"
    	let clientOne = Client(name: "Chill Client")
    	let repository = UserDefaultsRepository(
        	tableName: "Clients Database",
        	dataTransformer: JSONDataTransformer()
    	)
    	try await repository.create(id: clientRecordIdentifier, item: clientOne)
    	var clientRecord: Client = try await repository.read(id: clientRecordIdentifier)
    	print("Client Name: \(clientRecord.name)")
    	clientRecord.name = "Busy Client"
    	try await repository.update(id: clientRecordIdentifier, item: clientRecord)
    	let updatedClient: Client = try await repository.read(id: clientRecordIdentifier)
    	print("Updated Client Name: \(updatedClient.name)")
    	try await repository.delete(id: clientRecordIdentifier)
    	let removedClientRecord: Client = try await repository.read(id: clientRecordIdentifier)
    	print(removedClientRecord)
    }
    catch {
    	print(error.localizedDescription)
    }
    
    print("Array access example")
    
    let clientArrayRecordIdentifier = "clientArray"
    let clientOne = Client(name: "Chill Client")
    let repository = UserDefaultsRepository(
    	tableName: "Clients Database",
    	dataTransformer: JSONDataTransformer()
    )
    let array = [clientOne]
    try await repository.create(id: clientArrayRecordIdentifier, item: array)
    let savedArray: [Client] = try await repository.read(id: clientArrayRecordIdentifier)
    print(savedArray.first!)
    

    最初の CRUD チェック中に例外処理が実装されており、リモート アイテムの読み取りは利用できなくなります。

    データベースの切り替え

    次に、現在のコードを別のデータベースに転送する方法を説明します。たとえば、ChatGPT が生成した SQLite リポジトリ コードを取り上げます。

    
    class SQLiteRepository: CRUDRepository {
        private typealias RecordIdentifier = String
        
        let tableName: String
        let dataTransformer: DataTransformer
        private var db: OpaquePointer?
    
        init(
       	 tableName: String,
       	 dataTransformer: DataTransformer = JSONDataTransformer()
        ) {
       	 self.tableName = tableName
       	 self.dataTransformer = dataTransformer
       	 self.db = openDatabase()
       	 createTableIfNeeded()
        }
        
        private func openDatabase() -> OpaquePointer? {
       	 var db: OpaquePointer? = nil
       	 let fileURL = try! FileManager.default
       		 .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
       		 .appendingPathComponent("\(tableName).sqlite")
       	 if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
       		 print("error opening database")
       		 return nil
       	 }
       	 return db
        }
        
        private func createTableIfNeeded() {
       	 let createTableString = """
       	 CREATE TABLE IF NOT EXISTS \(tableName) (
       	 id TEXT PRIMARY KEY NOT NULL,
       	 data BLOB NOT NULL
       	 );
       	 """
       	 var createTableStatement: OpaquePointer? = nil
       	 if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
       		 if sqlite3_step(createTableStatement) == SQLITE_DONE {
           		 print("\(tableName) table created.")
       		 } else {
           		 print("\(tableName) table could not be created.")
       		 }
       	 } else {
       		 print("CREATE TABLE statement could not be prepared.")
       	 }
       	 sqlite3_finalize(createTableStatement)
        }
        
        private func isExists(id: CRUDRepository.ItemIdentifier) async throws -> Bool {
       	 let queryStatementString = "SELECT data FROM \(tableName) WHERE id = ?;"
       	 var queryStatement: OpaquePointer? = nil
       	 if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
       		 sqlite3_bind_text(queryStatement, 1, id, -1, nil)
       		 if sqlite3_step(queryStatement) == SQLITE_ROW {
           		 sqlite3_finalize(queryStatement)
           		 return true
       		 } else {
           		 sqlite3_finalize(queryStatement)
           		 return false
       		 }
       	 } else {
       		 print("SELECT statement could not be prepared.")
       		 throw CRUDRepositoryError.databaseError
       	 }
        }
        
        func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
       	 let insertStatementString = "INSERT INTO \(tableName) (id, data) VALUES (?, ?);"
       	 var insertStatement: OpaquePointer? = nil
       	 if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
       		 let data = try await dataTransformer.encode(item)
       		 sqlite3_bind_text(insertStatement, 1, id, -1, nil)
       		 sqlite3_bind_blob(insertStatement, 2, (data as NSData).bytes, Int32(data.count), nil)
       		 if sqlite3_step(insertStatement) == SQLITE_DONE {
           		 print("Successfully inserted row.")
       		 } else {
           		 print("Could not insert row.")
           		 throw CRUDRepositoryError.databaseError
       		 }
       	 } else {
       		 print("INSERT statement could not be prepared.")
       		 throw CRUDRepositoryError.databaseError
       	 }
       	 sqlite3_finalize(insertStatement)
        }
        
        func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T {
       	 let queryStatementString = "SELECT data FROM \(tableName) WHERE id = ?;"
       	 var queryStatement: OpaquePointer? = nil
       	 var item: T?
       	 if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
       		 sqlite3_bind_text(queryStatement, 1, id, -1, nil)
       		 if sqlite3_step(queryStatement) == SQLITE_ROW {
           		 let queryResultCol1 = sqlite3_column_blob(queryStatement, 0)
           		 let queryResultCol1Length = sqlite3_column_bytes(queryStatement, 0)
           		 let data = Data(bytes: queryResultCol1, count: Int(queryResultCol1Length))
           		 item = try await dataTransformer.decode(data: data)
       		 } else {
           		 throw CRUDRepositoryError.recordNotFound(id: id)
       		 }
       	 } else {
       		 print("SELECT statement could not be prepared")
       		 throw CRUDRepositoryError.databaseError
       	 }
       	 sqlite3_finalize(queryStatement)
       	 return item!
        }
        
        func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
       	 guard try await isExists(id: id) else {
       		 throw CRUDRepositoryError.recordNotFound(id: id)
       	 }
       	 let updateStatementString = "UPDATE \(tableName) SET data = ? WHERE id = ?;"
       	 var updateStatement: OpaquePointer? = nil
       	 if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK {
       		 let data = try await dataTransformer.encode(item)
       		 sqlite3_bind_blob(updateStatement, 1, (data as NSData).bytes, Int32(data.count), nil)
       		 sqlite3_bind_text(updateStatement, 2, id, -1, nil)
       		 if sqlite3_step(updateStatement) == SQLITE_DONE {
           		 print("Successfully updated row.")
       		 } else {
           		 print("Could not update row.")
           		 throw CRUDRepositoryError.databaseError
       		 }
       	 } else {
       		 print("UPDATE statement could not be prepared.")
       		 throw CRUDRepositoryError.databaseError
       	 }
       	 sqlite3_finalize(updateStatement)
        }
        
        func delete(id: CRUDRepository.ItemIdentifier) async throws {
       	 guard try await isExists(id: id) else {
       		 throw CRUDRepositoryError.recordNotFound(id: id)
       	 }
       	 let deleteStatementString = "DELETE FROM \(tableName) WHERE id = ?;"
       	 var deleteStatement: OpaquePointer? = nil
       	 if sqlite3_prepare_v2(db, deleteStatementString, -1, &deleteStatement, nil) == SQLITE_OK {
       		 sqlite3_bind_text(deleteStatement, 1, id, -1, nil)
       		 if sqlite3_step(deleteStatement) == SQLITE_DONE {
           		 print("Successfully deleted row.")
       		 } else {
           		 print("Could not delete row.")
           		 throw CRUDRepositoryError.databaseError
       		 }
       	 } else {
       		 print("DELETE statement could not be prepared.")
       		 throw CRUDRepositoryError.databaseError
       	 }
       	 sqlite3_finalize(deleteStatement)
        }
    }
    

    または、同じく ChatGPT によって生成された、ファイル システムのリポジトリの CRUD コード:

    
    class FileSystemRepository: CRUDRepository {
    	private typealias RecordIdentifier = String
        
    	let directoryName: String
    	let dataTransformer: DataTransformer
    	private let fileManager = FileManager.default
    	private var directoryURL: URL
        
    	init(
        	directoryName: String = "Database",
        	dataTransformer: DataTransformer = JSONDataTransformer()
    	) {
        	self.directoryName = directoryName
        	self.dataTransformer = dataTransformer
       	 
        	let paths = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
        	directoryURL = paths.first!.appendingPathComponent(directoryName)
       	 
        	if !fileManager.fileExists(atPath: directoryURL.path) {
            	try? fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
        	}
    	}
        
    	private func fileURL(id: CRUDRepository.ItemIdentifier) -> URL {
        	return directoryURL.appendingPathComponent("item_\(id).json")
    	}
        
    	private func isExists(id: CRUDRepository.ItemIdentifier) async throws -> Bool {
        	return fileManager.fileExists(atPath: fileURL(id: id).path)
    	}
        
    	func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
        	let data = try await dataTransformer.encode(item)
        	let url = fileURL(id: id)
        	try data.write(to: url)
    	}
        
    	func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T {
        	let url = fileURL(id: id)
        	guard let data = fileManager.contents(atPath: url.path) else {
            	throw CRUDRepositoryError.recordNotFound(id: id)
        	}
        	let item: T = try await dataTransformer.decode(data: data)
        	return item
    	}
        
    	func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
        	guard try await isExists(id: id) else {
            	throw CRUDRepositoryError.recordNotFound(id: id)
        	}
        	let data = try await dataTransformer.encode(item)
        	let url = fileURL(id: id)
        	try data.write(to: url)
    	}
        
    	func delete(id: CRUDRepository.ItemIdentifier) async throws {
        	guard try await isExists(id: id) else {
            	throw CRUDRepositoryError.recordNotFound(id: id)
        	}
        	let url = fileURL(id: id)
        	try fileManager.removeItem(at: url)
    	}
    }
    

    クライアント コード内のリポジトリを置き換えます。

    
    print("One item access example")
    
    do {
    	let clientRecordIdentifier = "client"
    	let clientOne = Client(name: "Chill Client")
    	let repository = FileSystemRepository(
        	directoryName: "Clients Database",
        	dataTransformer: JSONDataTransformer()
    	)
    	try await repository.create(id: clientRecordIdentifier, item: clientOne)
    	var clientRecord: Client = try await repository.read(id: clientRecordIdentifier)
    	print("Client Name: \(clientRecord.name)")
    	clientRecord.name = "Busy Client"
    	try await repository.update(id: clientRecordIdentifier, item: clientRecord)
    	let updatedClient: Client = try await repository.read(id: clientRecordIdentifier)
    	print("Updated Client Name: \(updatedClient.name)")
    	try await repository.delete(id: clientRecordIdentifier)
    	let removedClientRecord: Client = try await repository.read(id: clientRecordIdentifier)
    	print(removedClientRecord)
    }
    catch {
    	print(error.localizedDescription)
    }
    
    print("Array access example")
    
    let clientArrayRecordIdentifier = "clientArray"
    let clientOne = Client(name: "Chill Client")
    let repository = FileSystemRepository(
    	directoryName: "Clients Database",
    	dataTransformer: JSONDataTransformer()
    )
    let array = [clientOne]
    try await repository.create(id: clientArrayRecordIdentifier, item: array)
    let savedArray: [Client] = try await repository.read(id: clientArrayRecordIdentifier)
    print(savedArray.first!)
    

    UserDefaultsRepository の初期化は、適切な引数を備えた FileSystemRepository に置き換えられました。
    クライアント コードの 2 番目のバージョンを実行すると、ドキュメント フォルダーに「Clients Database」ディレクトリが表示されます。このディレクトリには、1 つのクライアント構造を持つ JSON でシリアル化された配列のファイルが含まれています。

    データ保存形式の切り替え

    次に、ChatGPT に XML のエンコーダーとデコーダーを生成するよう依頼しましょう。

    	let formatExtension = "xml"
        
    	func encode<T: Encodable>(_ item: T) async throws -> Data {
        	let encoder = PropertyListEncoder()
        	encoder.outputFormat = .xml
        	return try encoder.encode(item)
    	}
        
    	func decode<T: Decodable>(data: Data) async throws -> T {
        	let decoder = PropertyListDecoder()
        	return try decoder.decode(T.self, from: data)
    	}
    }
    

    Swift の組み込み型のおかげで、ニューラル ネットワークのタスクが初歩的になります。

    クライアント コードで JSON を XML に置き換えます。

    
    print("One item access example")
    
    do {
    	let clientRecordIdentifier = "client"
    	let clientOne = Client(name: "Chill Client")
    	let repository = FileSystemRepository(
        	directoryName: "Clients Database",
        	dataTransformer: XMLDataTransformer()
    	)
    	try await repository.create(id: clientRecordIdentifier, item: clientOne)
    	var clientRecord: Client = try await repository.read(id: clientRecordIdentifier)
    	print("Client Name: \(clientRecord.name)")
    	clientRecord.name = "Busy Client"
    	try await repository.update(id: clientRecordIdentifier, item: clientRecord)
    	let updatedClient: Client = try await repository.read(id: clientRecordIdentifier)
    	print("Updated Client Name: \(updatedClient.name)")
    	try await repository.delete(id: clientRecordIdentifier)
    	let removedClientRecord: Client = try await repository.read(id: clientRecordIdentifier)
    	print(removedClientRecord)
    }
    catch {
    	print(error.localizedDescription)
    }
    
    print("Array access example")
    
    let clientArrayRecordIdentifier = "clientArray"
    let clientOne = Client(name: "Chill Client")
    let repository = FileSystemRepository(
    	directoryName: "Clients Database",
    	dataTransformer: XMLDataTransformer()
    )
    let array = [clientOne]
    try await repository.create(id: clientArrayRecordIdentifier, item: array)
    let savedArray: [Client] = try await repository.read(id: clientArrayRecordIdentifier)
    print(savedArray.first!)
    

    クライアント コードは 1 つの式 JSONDataTransformer -> のみに変更されました。 XMLDataTransformer

    合計

    CRUD リポジトリは、アプリケーション アーキテクチャ コンポーネントの疎結合を実装するために使用できる設計パターンの 1 つです。もう 1 つの解決策 – ORM (Object-Relational Mapping) を使用すると、ORM は構造をデータベースに完全にマッピングし、モデルによる変更をデータベース上に表示 (マッピング (!)) するアプローチを使用します。
    しかし、それはまったく別の話です。

    Swift 用の CRUD リポジトリの完全な実装は、次の場所から入手できます。
    https://gitlab.com/demensdeum/crud-example

    ところで、Swift は macOS 以外でも長い間サポートされてきましたが、この記事のコードはすべて Arch Linux 上で作成され、テストされました。

    ソース

    https://developer.apple.com/documentation/combine/topleveldecoder
    https://developer.apple.com/documentation/combine/toplevelencoder
    https://en.wikipedia.org/wiki/Create,_read,_update_and_delete

    dd 入出力エラー

    Linux で dd を使用して通常のディスクをコピーするときに入出力エラーが発生した場合はどうすればよいですか?

    シトゥエヴィナは非常に悲しいですが、解決可能です。おそらく、使用、書き込み、読み取りができなくなった不良ブロックを含む故障したディスクを扱っていると考えられます。

    そのようなディスクは必ず S.M.A.R.T. を使用してチェックしてください。ディスク エラーが表示される可能性が高くなります。私の場合はこれに当てはまりました。不良ブロックの数が非常に多かったので、古いハードドライブに別れを告げて、新しい SSD に交換する必要がありました。

    問題は、このディスクには、作業に必要なライセンスされたソフトウェアを備えた完全に動作するシステムが含まれていることです。 partimage を使用してデータをすばやくコピーしようとしましたが、ユーティリティがディスクの 3 分の 1 だけをコピーし、セグメンテーション違反かその他の面白い Sishny/Sipplusplus ジョークで終了することに突然気づきました。

    次に、ddを使用してデータをコピーしようとしましたが、ddがpartimageとほぼ同じ場所に到達し、入出力エラーが発生することがわかりました。同時に、conv=noerr、skip などのあらゆる種類の面白いフラグはまったく役に立ちませんでした。

    しかし、ddrescue という GNU ユーティリティを使用すると、データは問題なく別のディスクにコピーされました。

    После этого мои волосы стали шелковистыми, вернулась жена, дети и собака перестала кусать диван.

    Большим плюсом ddrescue является наличие встроенного прогрессбара, поэтому не приходится костылять какие-то ухищрения навроде pv и всяких не особо красивых флажков dd. Также ddrescure показывает количество попыток прочитать данные; еще на вики написано что утилита обладает каким-то сверх алгоритмом для считывания поврежденных данных, оставим это на проверку людям которые любят ковыряться в исходниках, мы же не из этих да?

    https://ru.wikipedia.org/wiki/Ddrescue
    https://www.gnu.org/software/ddrescue/ddrescue_ru.html

    チャットGPT

    皆さん、こんにちは!この記事では、ChatGPT – について話したいと思います。 OpenAI の強力な言語モデリングは、テキスト処理のさまざまな問題の解決に役立ちます。このツールがどのように機能し、実際の状況でどのように使用できるかを説明します。始めましょう!

    現時点では、ChatGPT はニューラル ネットワークに基づく世界最高の言語モデルの 1 つです。これは、開発者が自然言語を生成し、その中で人々とコミュニケーションできるインテリジェント システムを作成できるように支援することを目的として作成されました。

    ChatGPT の主な利点の 1 つは、テキストを状況に応じてモデル化できることです。これは、モデルが以前の対話を考慮し、それを使用して状況をより正確に理解し、より自然な応答を生成することを意味します。

    ChatGPT は、カスタマー サポートの自動化、チャットボットの作成、テキスト生成など、さまざまなタスクに使用できます。

    ChatGPT の背後にあるニューラル ネットワークは、非常に正確な予測を保証するために、大量のテキストでトレーニングされました。これにより、モデルは対話をサポートし、質問に答えることができる自然なテキストを生成できるようになります。

    ChatGPT を使用すると、自然言語で人々と対話できる独自のチャットボットやその他のインテリジェント システムを作成できます。これは、観光、小売、カスタマー サポートなどの業界で特に役立ちます。

    結論として、ChatGPT –これは、さまざまな言語モデリングの問題を解決するための強力なツールです。コンテキスト モデリングの機能により、チャットボットやインテリジェント システムの作成に特に役立ちます。


    実際、ChatGPT は上記のすべてを完全に単独で記述しました。何?はい?私自身もショックを受けています!

    ここでグリッド自体を試すことができます。
    https://chat.openai.com/chat

    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