「技術」カテゴリーアーカイブ

【C#】【アルゴリズム】マージソート

テキストに書いてあったマージソートのアルゴリズムです。

        public void MargeSort(List<int> A)
        {
            List<int> C = new List<int>();
            for(int i = 0; i < A.Count; i++)
            {
                C.Add(0);
            }
            MargeSortSub(A, C, 0, A.Count);
        }

        public void MargeSortSub(List<int> A, List<int> C, int l, int r)
        {
            if(r - l == 1)
            {
                return;
            }

            int m = (l + r) / 2;
            MargeSortSub(A, C, l, m);
            MargeSortSub(A, C, m, r);

            int c1 = l;
            int c2 = m;
            int cnt = 0;

            while(c1 != m || c2 != r)
            {
                if (c1 == m)
                {
                    C[cnt] = A[c2];
                    c2++;
                }else if (c2 == r)
                {
                    C[cnt] = A[c1];
                    c1++;
                }else
                {
                    if(A[c1] <= A[c2])
                    {
                        C[cnt] = A[c1];
                        c1++;
                    }else
                    {
                        C[cnt] = A[c2];
                        c2++;
                    }
                }
                cnt++;
            }

            for(int i = 0; i < cnt; i++)
            {
                A[l + i] = C[i];
            }
        }

RUST勉強中、10/9の積み上げ

前回の続きで、Rust側の実装を行って行きます。

[package]
name = "mandelbrot"
version = "0.1.0"
authors = ["taki"]
edition = "2018"

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook"]

[dependencies]
js-sys = "0.3.60"
wasm-bindgen = "0.2.63"

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }

# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.5", optional = true }

[dependencies.web-sys]
version = "0.3.60"
features = [
    'CanvasRenderingContext2d',
    'Document',
    'Element',
    'HtmlCanvasElement',
    'ImageData',
    'Performance',
    'Window',
]

[dev-dependencies]
wasm-bindgen-test = "0.3.13"

[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
mod utils;
mod logic;

use wasm_bindgen::prelude::*;
use wasm_bindgen::{Clamped, JsCast};
use web_sys::ImageData;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(a: &str);
}

macro_rules! console_log {
    ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}

macro_rules! measure_elapsed_time {
    ($t:tt,$s:block) => {{
        let window = web_sys::window().expect("should have a window in this context");
        let performance = window
            .performance()
            .expect("performance should be available");
        let start = performance.now();
        let result = { $s };
        let end = performance.now();
        console_log!("{}:{}[ms]", $t, end - start);
        result
    }};
}

#[wasm_bindgen]
pub fn generate_mandelbrot_set(
    canvas_w: usize,
    canvas_h: usize,
    x_min: f64,
    x_max: f64,
    y_min: f64,
    y_max: f64,
    max_iter: usize,
) -> Vec<u8> {
    measure_elapsed_time!("generate:wasm\telapsed:", {
        logic::generate_mandelbrot_set(canvas_w, canvas_h,
            x_min, x_max, y_min, y_max, max_iter)
    })
}

#[wasm_bindgen]
pub fn draw_mandelbrot_set() {
    const CANVAS_ID: &str = "canvas_wasm";
    let document = web_sys::window().unwrap().document().unwrap();
    let canvas = document.get_element_by_id(CANVAS_ID).unwrap();
    let canvas: web_sys::HtmlCanvasElement = canvas
        .dyn_into::<web_sys::HtmlCanvasElement>()
        .map_err(|_| ())
        .unwrap();
    let context = canvas
        .get_context("2d")
        .unwrap()
        .unwrap()
        .dyn_into::<web_sys::CanvasRenderingContext2d>()
        .unwrap();
    let canvas_w = canvas.width() as usize;
    let canvas_h = canvas.height() as usize;
    const X_MIN: f64 = -1.5;
    const X_MAX: f64 = 0.5;
    const Y_MIN: f64 = -1.0;
    const Y_MAX: f64 = 1.0;
    const MAX_ITER: usize = 64;

    let mut result = measure_elapsed_time!("generate:wasm\telapsed:", {
        logic::generate_mandelbrot_set(canvas_w, canvas_h,
            X_MIN, X_MAX, Y_MIN, Y_MAX, MAX_ITER)
    });
    measure_elapsed_time!("draw:wasm\telapsed:", {
        let data = ImageData::new_with_u8_clamped_array_and_sh(
            Clamped(&mut result),
            canvas.width(),
            canvas.height(),
        );
        if let Ok(data) = data {
            let _ = context.put_image_data(&data, 0.0, 0.0);
        }
    })
}

マジ疲れたわ。

JavaScript側でスペルミスがあって、うまくJS側からRustの処理を呼び出せないというミスをやらかして、

スペルミスに気がついて対処して動かすまでに1時間ぐらいかかったかな?

原因を探るためにWinMargeをインストールして、サンプルプログラムと書いたコードを比較して、と言う作業をやっていました。

【数学】【PYTHON】三角関数を使って円を描画する

import matplotlib.pyplot as plt
import numpy as np

#角度
th = np.arange(0, 360)

#円周上の点Pの座標
x = np.cos(np.radians(th))
y = np.sin(np.radians(th))

#描画
plt.plot(x, y)
plt.axis('equal')
plt.grid(color='0.8')
plt.show()

角度0~360に対して

x座標をコサインで、y座標をサインで計算します。

このとき、引数の角度はラジアンで与える必要があります。

RUST勉強中、10/2の積み上げ?

前回の続き

www/index.htmlを書いていきます。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hello wasm-pack!</title>
  </head>
  <body>
    <noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
    <button id="render">render</button>
    <div>
      <canvas id="canvas_wasm" height="1200" width="1200"></canvas>
      <canvas id="canvas_js" height="1200" width="1200"></canvas>
      <canvas id="canvas_hybrid" height="1200" width="1200"></canvas>
    </div>
    <script src="./bootstrap.js"></script>
  </body>
</html>

www/index.js

'use strict'

function draw(ctx, canvas_w, canvas_h, data) {
    let img = new ImageData(new Uint8ClampedArray(data), canvas_w, canvas_h);
    ctx.putImageData(img, 0, 0,);
}

const X_MIN = -1.5;
const X_MAX = 0.5;
const Y_MIN = -1.0;
const Y_MAX = 1.0;
const MAX_ITER = 64;

console.log("start loading wasm");
const mandelbrot = import('../pkg').catch(console.error);

Promise.all([mandelbrot]).then(async function([
    {generate_mandelbrot_set,draw_mandelbrot_set}
]) {
    console.log("finished loading wasm");
    const renderBtn = document.getElementById('render');
    renderBtn.addEventListener('click', () => {
        draw_mandelbrot_set();
        let wasmResult = null;
        {
            const CANVAS_ID = "canvas_hybrid";
            let canvas = document.getElementById(CANVAS_ID);
            let context = canvas.getContext("2d");
            const canvasWidth = canvas.width;
            const canvasHeight = canvas.height;

            const generateStartTime = Date.now();
            wasmResult = generate_mandelbrot_set(canvasWidth, canvasHeight,
                X_MIN, X_MAX, Y_MIN, Y_MAX, MAX_ITER);
            const generateEndTime = Date.now();
            const drawStartTime = Date.now();
            draw(context, canvasWidth, canvasHeight, wasmResult);
            const drawEndTime = Date.now();
            const elapsed = generateEndTime - generateStartTime;
            console.log(`\tgenerate:wasm\tgenerate_elapsed:${elapsed} [ms]`);
            console.log(`\tdraw: js\tdraw_elapsed] ${drawEndTime - drawStartTime} [ms]`);
        }
    })
})

処理の呼び出し側のコードなんだけど、Rustのコード書いてねー!

【数学】【PYTHON】垂直二等分線を求める

これまでやってきたことを使って、二つの点の線分の中間を垂直に交わる直線を求めます。

例として、(0,1)と(6,5)を結ぶ線分の垂直二等分線を求めてみます。

やり方は、

①線分の傾きを求める

②線分の中点を求める

③線分と直交する直線の傾きを求める

④二つの線の切片を求める

で求めることができるみたいです。

import matplotlib.pyplot as plt
import numpy as np

#元の直線の傾きと切片
a1 = (5-1)/(6-0)
b1 = 1

#線分の中点
cx = (0 + 6) / 2
cy = (1 + 5) / 2

#線分に直交する直線の傾き
a2 = -1 / a1

#線分に直交する直線の切片
b2 = cy - a2 * cx

#直線の式
x = np.arange(0, 7)
y1 = a1*x + b1
y2 = a2*x + b2

plt.plot(x, y1)
plt.plot(x, y2)
plt.axis('equal')
plt.grid(color='0.8')
plt.show()

RUST勉強中、9/19の積み上げ?

マンデルブロ集合の処理を実装していきます。

まぁ、この処理は難解なので、ここはテキストを写経していきます。

目標はテストコードをパスするところまで。

fn get_n_driverged(x0: f64, y0: f64, max_iter: usize) -> u8 {
    let mut xn = 0.0;
    let mut yn = 0.0;
    for i in 1..max_iter {
        let x_next = xn * xn - yn * yn + x0;
        let y_next = 2.0 * xn * yn + y0;
        xn = x_next;
        yn = y_next;
        if yn * yn + xn * xn > 4.0 {
            return i as u8;
        }
    }
    max_iter as u8
}

pub fn generate_mandalbrot_set(
    canvas_w: usize,
    canvas_h: usize,
    x_min: f64,
    x_max: f64,
    y_min: f64,
    y_max: f64,
    max_iter: usize,
) -> Vec<u8> {
    let canvas_w_f64 = canvas_w as f64;
    let canvas_h_f64 = canvas_h as f64;
    let mut data = vec![];
    for i in 0..canvas_h {
        let i_f64 = i as f64;
        let y = y_min + (y_max - y_min) * i_f64 / canvas_h_f64;
        for j in 0..canvas_w {
            let x = x_min + (x_max - x_min) * j as f64 / canvas_w_f64;
            let iter_index = get_n_driverged(x, y, max_iter);
            let v = iter_index % 8 * 32;
            data.push(v);
            data.push(v);
            data.push(v);
            data.push(255);
        }
    }
    data
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_get_n_diverged() {
        let max_iter = 10;
        assert_eq!(get_n_driverged(1.0, 0.0, max_iter), 3);
        assert_eq!(get_n_driverged(0.0, 0.0, max_iter), max_iter as u8);
        assert_eq!(get_n_driverged(0.0, 1.0, max_iter), max_iter as u8);
    }
    #[test]
    fn test_generate_mandelbrot_set() {
        let canvas_w = 2;
        let canvas_h = 2;
        let x_min = -1.0;
        let x_max = 1.0;
        let y_min = -1.0;
        let y_max = 1.0;
        let max_iter = 8;
        assert_eq!(
            generate_mandalbrot_set(canvas_w, canvas_h, x_min, x_max, y_min, y_max, max_iter),
            vec![96, 96, 96, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255]
        );
    }
}
:~/rust/mandelbrot$ cargo test
   Compiling mandelbrot v0.1.0 (/home/taki/rust/mandelbrot)
warning: function `set_panic_hook` is never used
 --> src/utils.rs:1:8
  |
1 | pub fn set_panic_hook() {
  |        ^^^^^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: function `get_n_driverged` is never used
 --> src/logic.rs:1:4
  |
1 | fn get_n_driverged(x0: f64, y0: f64, max_iter: usize) -> u8 {
  |    ^^^^^^^^^^^^^^^

warning: function `generate_mandalbrot_set` is never used
  --> src/logic.rs:16:8
   |
16 | pub fn generate_mandalbrot_set(
   |        ^^^^^^^^^^^^^^^^^^^^^^^

warning: `mandelbrot` (lib) generated 3 warnings
warning: `mandelbrot` (lib test) generated 1 warning (1 duplicate)
    Finished test [unoptimized + debuginfo] target(s) in 0.46s
     Running unittests src/lib.rs (target/debug/deps/mandelbrot-c2f466aeb790a116)

running 2 tests
test logic::tests::test_generate_mandelbrot_set ... ok
test logic::tests::test_get_n_diverged ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/web.rs (target/debug/deps/web-ca9c3937d615c7ed)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests mandelbrot

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

【数学】【PYTHON】二つの直線の交点を求める

二つの直線の交点を求めるには、

二つの直線の連立方程式を解けば求めることができます。

連立方程式はこちらでやってますね。

例えば、y = -3/2x + 6とy= 1/2x + 2の交点を求めてみました。

from sympy import Symbol, solve

x = Symbol('x')
y = Symbol('y')
ex1 = -3/2*x + 6 - y
ex2 = 1/2*x + 2 - y

print(solve((ex1, ex2)))
:~/python$ python3 simultaneous_equations.py 
{x: 2.00000000000000, y: 3.00000000000000}