2017年11月6日月曜日

ギヤ比

ソースコード
https://github.com/index333/gear

ギヤ比は一般には単に前後の比率で表します。
例えばフロント30T x リヤ20Tの場合
30➗20で1.50

欧米では伝統的にダルマ型自転車の前輪径に換算します。
30x20の場合
27x1.5=41インチギヤ
のように表現します。
欧米式の利点は小径車にも統一的に使えることです。
上の場合
20x1.5=30インチギヤとなります。
逆にロードレーサーの41インチギヤと同じ速度を小径車で得るには
30x15あるいは39x20という前後の組み合わせが必要となります。


ケーデンスを固定した時の時速で表したほうが直感的と思います。

まずタイヤ周長を選択します。

import Graphics.UI.Gtk
main = do
c <- getContents
let texts = lines c
initGUI
w <- windowNew
w `set` [windowDefaultWidth := 1000, windowDefaultHeight := 800]
sw <- scrolledWindowNew Nothing Nothing
vb <- vBoxNew True 0
bs <- mapM (buttonNewWithLabel) texts
mapM_ (\b -> b `set` [buttonXalign := 0, buttonYalign := 0]) bs
mapM_ (\b -> (b `on` buttonActivated) (func b)) bs
mapM_ (containerAdd vb) bs
scrolledWindowAddWithViewport sw vb
containerAdd w sw
widgetShowAll w
(w `on`unrealize) mainQuit
mainGUI
func b = b `get` buttonLabel >>= putStrLn >> mainQuit
view raw selectItem.hs hosted with ❤ by GitHub
データはキャットアイのページからコピーしました。

$ cat tires
18-622 700x18C 2070 207
19-622 700x19C 2080 208
20-622 700x20C 2086 209
23-622 700x23C 2096 210
25-622 700x25C 2105 211
28-622 700x28C 2136 214
30-622 700x30C 2146 215
32-622 700x32C 2155 216
xx-xxx  Tubular 2130 213
35-622 700x35C 2168 217
38-622 700x38C 2180 218
40-622 700x40C 2200 220
42-622 700x42C 2224 222
44-622 700x44C 2235 224
45-622 700x45C 2242 224
47-622 700x47C 2268 227

$ cat tires | runghc selectItem.hs > tmp
$ cat tmp
23-622 700x23C 2096 210
import Graphics.UI.Gtk
import MySpinBox
import Round
main = do
c <- getContents
let [_,_,a,_] = words c
let t = read a :: Double
let names = ["タイヤ周長(mm)",
"frontA(T)",
"frontB(T)",
"最大コグ(T)",
"cadens(/m)"]
let spmods = [(t,2000,2300,1,10),
(39,20,60,1,10),
(52,20,60,1,10),
(23,21,32,1,10),
(90,50,160,1,10)]
initGUI
window <- windowNew
hbox <- hBoxNew False 0
vbox <- vBoxNew False 0
boxPackStart vbox hbox PackNatural 0
adjs <- mkAdjustments spmods
update adjs
spins <- myAddSpinButtons hbox names adjs
mapM_ (`set` [spinButtonDigits := 0]) spins
mapM_ (flip onValueChanged (update adjs)) adjs
containerAdd window vbox
widgetShowAll window
window `on` unrealize $ mainQuit
mainGUI
update adjs = do
l:fa:fb:m:c:_ <- mapM (`get` adjustmentValue) adjs
putStr "タイヤ周長(mm) = "
print l
mapM_ (disp l fa fb c) [11..m]
disp l fa fb c x = do
disp' l fa x c
putStr " "
disp' l fb x c
putStrLn ""
disp' l f r c = do
putStr $ show $ round f
putStr "x"
putStr $ show $ round r
putStr "="
putStr $ show (kph l f r c)
putStr "km/h "
kph l f r c = round1 $ (l/1000^2) * (f / r) * (c * 60)
view raw gear.hs hosted with ❤ by GitHub


Round モジュール、MySpinBox モジュールが必要です。
module Round where
roundN :: Int -> Double -> Double
roundN n d = fromIntegral (round (d * 10 ^ n)) / fromIntegral (10 ^ n)
round1,round2 :: Double -> Double
round1 = roundN 1
round2 = roundN 2
view raw Round.hs hosted with ❤ by GitHub
module MySpinBox where
import Graphics.UI.Gtk
import Control.Monad
mkAdjustment :: (Double, Double, Double, Double, Double) -> IO Adjustment
mkAdjustment (v,l,u,s,p) = adjustmentNew v l u s p 0
{-
:: Double value - the initial value.
-> Double lower - the minimum value.
-> Double upper - the maximum value.
-> Double stepIncrement - the step increment.
-> Double pageIncrement - the page increment.
-> Double pageSize - the page size.
-}
mkAdjustments :: [(Double, Double, Double, Double, Double)] ->
IO [Adjustment]
mkAdjustments = mapM mkAdjustment
myAddSpinButtons :: HBox -> [String] ->[Adjustment] -> IO [SpinButton]
myAddSpinButtons box names adjustments = do
zipWithM (\x y -> myAddSpinButton box x y) names adjustments
myAddSpinButton :: HBox -> String -> Adjustment -> IO SpinButton
myAddSpinButton box name adj = do
vbox <- vBoxNew False 0
boxPackStart box vbox PackRepel 0
label <- labelNew (Just name)
miscSetAlignment label 0.0 0.5
boxPackStart vbox label PackNatural 0
spinb <- spinButtonNew adj 10 1
boxPackStart vbox spinb PackGrow 0
return spinb
view raw MySpinBox.hs hosted with ❤ by GitHub
$ cat tmp | runghc gear.hs

2017年11月3日金曜日

pcd,歯底距離

チェーンリングのPCD(ピッチ・サークル・ダイアメーター)を計算します。
チェーンリングのPCDとはチェーン用ギヤにチェーンを巻きつけたときチェーンピンが形作る仮想円の直径のことです。
これは1辺の長さが12.7mmの正N角形に外接する円の直径の計算に還元できます。
(
クランクのPCDと区別するため、あちらはBCD(ボルト・サークル・ダイアメーター)と表現します
)
次に歯底距離を計算します。

--Ch.hs
module Ch (chlen,pitch) where
import Data.Complex
pitch = 12.7
p = pitch
chlen a b c = (l + f + r) * 2
where rf = pcr a
rr = pcr b
l' = rf - rr
l = sqrt(c ** 2 - l' ** 2)
th = atan (c / l')
sf = sirc a / 2
sr = sirc b / 2
f = sf * (pi - th) / pi
r = sr * th / pi
sirc = (p *)
rOfd = 7.8 / 2
yen = pi * 2
rd0 n | even n = (n,round (rd d))
| otherwise = (n,round (rd' d))
where d = fromIntegral n
rd n = pcd n - rOfd * 2
rd' n = let a = mkPolar m (pi/n/2)
b = mkPolar m pi
in dist a b
where m = pcr n-rOfd
dist a b = magnitude (a - b)
pcr n = d / sin t
where d = p / 2
t = pi / n
pcd n = pcr n * 2
view raw Ch.hs hosted with ❤ by GitHub
歯底距離を測ればチェーンリングの歯数がわかります。

$ ghci
Prelude> :l Ch.hs
[1 of 1] Compiling Ch               ( Ch.hs, interpreted )
Ok, modules loaded: Ch.
*Ch> rd0 43
(43,166)
*Ch> rd0 44
(44,170)

歯底距離が166mmであれば歯数は43T
歯底距離が170mmであれば歯数は44T

チェーリングのPCDとリヤスプロケットのPCDがわかれば、シングル・バイクのチェーン長が計算できます。

芯間距離(チェーンステイ長)405mm、前スプロケット歯数48Tそして後スプロケット歯数16Tの場合

*Ch> chlen 48 16 405
1226.491724535387
*Ch> it / p
96.57415153821945

97リンク。(半コマ使用時)
98リンク。(半コマ不使用時)

チェーン長計算は下図を参考にしてください。


SpinBoxで入力を楽に

初期データは「tmp]に

$ cat tmp
[48.0,16.0,405.0]



module MySpinBox where
import Graphics.UI.Gtk
import Control.Monad
mkAdjustment :: (Double, Double, Double, Double, Double) -> IO Adjustment
mkAdjustment (v,l,u,s,p) = adjustmentNew v l u s p 0
{-
:: Double value - the initial value.
-> Double lower - the minimum value.
-> Double upper - the maximum value.
-> Double stepIncrement - the step increment.
-> Double pageIncrement - the page increment.
-> Double pageSize - the page size.
-}
mkAdjustments :: [(Double, Double, Double, Double, Double)] ->
IO [Adjustment]
mkAdjustments = mapM mkAdjustment
myAddSpinButtons :: HBox -> [String] ->[Adjustment] -> IO [SpinButton]
myAddSpinButtons box names adjustments = do
zipWithM (\x y -> myAddSpinButton box x y) names adjustments
myAddSpinButton :: HBox -> String -> Adjustment -> IO SpinButton
myAddSpinButton box name adj = do
vbox <- vBoxNew False 0
boxPackStart box vbox PackRepel 0
label <- labelNew (Just name)
miscSetAlignment label 0.0 0.5
boxPackStart vbox label PackNatural 0
spinb <- spinButtonNew adj 10 1
boxPackStart vbox spinb PackGrow 0
return spinb
view raw MySpinBox.hs hosted with ❤ by GitHub
import System.FilePath.Posix
import MySpinBox
import Graphics.UI.Gtk
import Ch
fn = "tmp"
main = do
l <- readFile fn
let [a,b,c] = read l::[Double]
initGUI
window <- windowNew
hbox <- hBoxNew False 0
vbox <- vBoxNew False 0
boxPackStart vbox hbox PackNatural 0
let spmods = [(a, 30, 60 ,1 , 10),
(b, 10, 30, 1, 10),
(c, 100, 1000 ,1, 10)]
let names = ["大ギヤ(T)" ,"小ギヤ(T)","rc(mm)"]
adjs <- mkAdjustments spmods
spins <- myAddSpinButtons hbox names adjs
mapM_ (`set` [spinButtonDigits := 0]) spins
mapM_ (\x-> onValueChanged x (update adjs)) adjs
containerAdd window vbox
widgetShowAll window
window `on` unrealize $ end adjs
mainGUI
update adjs = do
a:b:c:_ <- mapM (`get` adjustmentValue) adjs
print a
print b
print c
print $ chlen a b c / pitch
end adjs = do
a:b:c:_ <- mapM (`get` adjustmentValue) adjs
writeFile fn $ show [a,b,c]
mainQuit
view raw chlen.hs hosted with ❤ by GitHub

スプロケを交換したときに
リヤアクスルが何ミリ移動するか計算します。
--chFrame.hs
import Graphics.UI.Gtk
import Ch
import MySpinBox
import System.Random
fn = "tmp"
main = do
l <- readFile fn
print l
let [a,b,c] = read l::[Double]
let cl = chlen a b c
initGUI
window <- windowNew
hbox <- hBoxNew False 0
containerAdd window hbox
let names = ["大ギヤ(T)","小ギヤ(T)"]
let spmods = [(a, 30, 60 ,1,10),(b, 10, 30, 1, 10)]
adjs <- mkAdjustments spmods
spins <- myAddSpinButtons hbox names adjs
mapM_ (`set` [spinButtonDigits := 0]) spins
mapM_ (\x-> onValueChanged x (update adjs c cl)) adjs
widgetShowAll window
window `on` unrealize $ end adjs c cl
mainGUI
f adjs c cl = do
[a,b] <- mapM (`get` adjustmentValue) adjs
r <- try (pf a b cl) (c-50,c+50)
return ([a,b],r)
update adjs c cl = do
([a,b],r) <- f adjs c cl
disp c r (a,b)
end adjs c cl = do
([a,b],r) <- f adjs c cl
writeFile fn $ show [a,b,fromIntegral (round r)]
mainQuit
rnd l h = randomRIO (l,h) :: IO Double
e = 0.001
try f (l,h)= do
if abs (h -l) < e
then return h
else do
r <- rnd l h
if f r
then try f (r,h)
else try f (l,r)
disp c x (a,b) = do
putStr "rc. "
putStr $ show $ round c
putStr " -> "
putStr $ show $ round x
putStr " mm. 差 = "
putStrLn $ show $ round $ x - c
putStr "Ratio = "
putStrLn $ show $ a/b
pf a b cl c = cl > ch
where ch = chlen a b c
view raw chFrame.hs hosted with ❤ by GitHub

2017年11月1日水曜日

ランダム法

「Haskell98標準のrandomパッケージは、1.長くない周期、2.Haskell98由来の古いAPIの制約で内部状態を毎回コピーする、3.そのため遅い、4.CPU時間と現在時間で初期化(ポケモンかな???)など、過去に標準だったため多くの環境で使用出来ると考えられる、という点以外に余りお勧めできる所が有りません。」
(Haskellの乱数事情より)
https://qiita.com/philopon/items/8f647fc8dafe66b7381b

だそうですがここでは便利さをとり
 System.Random
モジュールを使います。

 System.Randomモジュールはcabalコマンドでパッケージを導入しなければなりません。
まず

sudo apt install cabal-install

でcabalをインストール。

$ cabal update -v
$ cabal install random -v

でImportできるようになります。
結構時間がかかるので -v オプションをつけて経過を見ながら。

なお表題の「ランダム法」はランダムを使っているという意味であり「モンテカルロ法」とは関係ありません。

基本のコードは

import System.Random
import Control.Monad.State
rnd l h = randomRIO (l,h) :: IO Double
try p = do
a@(l,h) <- get
r <- liftIO $ rnd l h
if p r
then put (r,h)
else put (l,r)
b <- get
if a == b
then return b
else try p
view raw randomMethod.hs hosted with ❤ by GitHub


これで、未知数が一つ、解の範囲があらかじめ推測できるという条件の方程式を解くことができます。

上のコードに

root n x = evalStateT (try (pf n x)) (1,x) >>= return
pf n x g = g^n < x
main = root 2 2 >>= print

を付け加えて実行しますと

$ runghc -XFlexibleContexts  randomMethod.hs
(1.4142135623728276,1.4142135623731693)

3角形の3辺の長さがわかっていれば、その形と大きさが決まることから面積を計算できるはずです。
ヘロンの公式です。
ちなみにヘロンの公式はHaskellでは極めて簡潔にこう書けます。
heron a b c = sqrt (s*(s-a)*(s-b)*(s-c)) where s = (a+b+c) / 2

余談ですがどんな複雑な形の土地でも3角形に分割することで巻き尺1本で面積計算ができます。
(ただし平面地)
ヘロンの公式が思い出せなくてもランダム法で直感的に解くことができます。

三角形の3辺の長さが a,b,c で

bを複素平面上の実数軸においたとき、
三角形の頂点は極座標で
(a,θ)
で表せます。
このθをランダム法で探ればよいのです。


import System.Random
import Data.Complex
import Control.Monad.State
rnd l h = randomRIO (l,h) :: IO Double
try p = do
a@(l,h) <- get
r <- liftIO $ rnd l h
if p r
then put (r,h)
else put (l,r)
b <- get
if a == b
then return b
else try p
heron a b c = do
(r,_) <- evalStateT (try (pf' a b c)) (0, pi)
let t = mkPolar a r
let h = imagPart t
return $ (b*h) / 2
pf' a b c g =
let c0 = mkPolar a g
c1 = b :+ 0
in magnitude (c0 - c1) < c
main = heron 3 4 5 >>= print
view raw heron.hs hosted with ❤ by GitHub


$ runghc -XFlexibleContexts  heron.hs
6.0

円に内接する正6角形と、同じ円に外接する正4角形を考えると、
円周率は3と4の間に存在するとわかります。
3と4の間の実数のどれかはπの近似値になるはずです。
では、それをランダム法で求められるでしょうか。
残念ながらできません。円周率を求める方程式が存在しないからです。

複素数を使えば間接的にπを求めることはできます。
複素数 c=-1+0i を極座標で表しますと (1、π)
位相を未知数θとして θを 3と 4 の間で、さまざまに変化させ c と近似する値をみつければよい。

でもこれはチートなのでは?

Haskell Process

Haskellの System.Processは便利ですが、問題もあります。 単一スレッドでの逐次処理を保証していない。(想像です。) 次のようなスクリプトを書いてみた。 --a.hs main = print [1..10] --t.hs import Sy...