裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

覆面算4

2017年03月31日 | ブログラミング

謹賀新年(2017年) 〜覆面算〜
http://quiz-tairiku.com/q.cgi?mode=view&no=18772

              とび
   ひのととり   ー たとう     
  + ひのとり   ー  たび
  + のように   ー とりの
  + とうとい   + ように
    
   2017に      29
定数はそのまま(他の文字がかぶっても構わない)


f = function() {
    library(e1071)
    a = permutations(10)-1
    a = a[a[,1] != 0 & a[,2] != 0 & a[,3] != 0 & a[,10] != 0 & a[,5] != 0,]
    a1 = a[,c(1,2,3,3,4)] %*% 10^(4:0)
    a2 = a[,1:4] %*% 10^(3:0)
    a3 = a[,c(2, 5:7)] %*% 10^(3:0)
    a4 = a[,c(3,6,3,8)] %*% 10^(3:0)
    a5 = 20170+a[,7]
    b1 = a[,c(3,9)] %*% 10^(1:0)
    b2 = a[,c(10,3,6)] %*% 10^(2:0)
    b3 = a[,c(10,9)] %*% 10^(1:0)
    b4 = a[,c(3,4,2)] %*% 10^(2:0)
    b5 = a[, 5:7] %*% 10^(2:0)
    i = which(a1+a2+a3+a4 == a5 & b1-b2-b3-b4+b5 == 29)
    cat(sprintf("%7d\n%7d\n%7d\n+%6d\n--------\n%7d\n\n", a1[i], a2[i], a3[i], a4[i], a5[i]))
    cat(sprintf("%7d\n-%6d\n-%6d\n-%6d\n+%6d\n--------\n%7d\n", b1[i], b2[i], b3[i], b4[i], b5[i], 29))
}
f()

  12338
   1238
   2967
+  3634
--------
  20177

     30
-   536
-    50
-   382
+   967
--------
     29

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

覆面算3

2017年03月30日 | ブログラミング

謹賀新年(2017年) 覆面算
http://quiz-tairiku.com/q.cgi?mode=view&no=18773

(1) きんが + しんねん = 2017 となり,2, 0, 1, 7 をすべて含むもの
(2) とりにく + とりにく + … + とりにく = やきとり
  すなわち,n(とりにく) = やきとり
  となり,解に 2, 9 両方が含まれるもの

----------------------------------------

f = function(x) {
    library(e1071)
    a = permutations(5)
    a = apply(a, 1, function(y) c(2, 0, 1, 7, x)[y])
    きんが = 10^(2:0) %*% a[c(1, 2, 3), ]
    しんねん = 10^(3:0) %*% a[c(4, 2, 5, 2), ]
    ans = きんが + しんねん == 2017
    if (any(ans)) {
        cat(sprintf("%6d\n+%5d\n------\n%6d\n", きんが[ans], しんねん[ans], 2017))
    }
}
for (x in c(3:6, 8, 9)) {
    f(x)
}

   270
+ 1747
------
  2017

g = function() {
    library(e1071)
    a = data.frame(permutations(10) - 1)
    a = as.matrix(unique(a[a[, 1] != 0 & a[, 5] != 0, 1:6]))
    w = 10^(3:0)
    とりにく = a[, c(1:4)] %*% w
    やきとり = a[, c(5, 6, 1, 2)] %*% w
    for (i in seq_along(とりにく)) {
        ans = which(1:9 * とりにく[i] == やきとり[i])
        if (any(ans) && # n 「とりにく」 = 「やきとり」
            ans %in% a[i, ] == FALSE && # ただし,n は「とりにくやき」に含まれない
            all(c(2, 9) %in% c(a[i, ], ans))) { # また,2, 9 を含む
            cat(sprintf("\n%6d\nx%5d\n------\n%6d\n", とりにく[i], ans, やきとり[i]))
        }
    }
}
g()

答えは一意に決まると書いてあいるが,そうではないようだ(やはり,「適切な」総当たりというのは必要)

  1402    これを唯一の解としているが
x    7
------
  9814

  1809    他にも 4 通りの解がある
x    2
------
  3618

  1859
x    2
------
  3718

  3819
x    2
------
  7638

  2709
x    3
------
  8127

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

数学の問題を R で解く(その2)

2017年03月29日 | ブログラミング

10 桁の数字 n(すなわち 1000000000 ≦ n ≦ 9999999999) において,n^n の末尾の 3 桁が 777 になるのは,何個あるか

1 桁の数字でそのようなものは,0 個
2 桁の数字でそのようなものは,0 個
3 桁の数字でそのようなものは,1 個
4 桁の数字でそのようなものは,9 個
  :

f = function(x) {
    y = 1
    for (i in 1:x) {
        y = (y*x) %% 1000
    }
    y == 777
}
sum(sapply(0:9, f)) # 0
sum(sapply(10:99, f)) # 0
sum(sapply(100:999, f)) # 1
sum(sapply(1000:9999, f)) # 9
# sum(sapply(1000000000:9999999999, f)) # oh! no!

 

10 桁の数字でそのようなものは,90000000 個

 

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

数学の問題を R で解く

2017年03月29日 | ブログラミング

スコットランドの高校生(16〜18歳)対象の数学問題

1匹のワニが20メートル上流の川の対岸にいる獲物に忍び寄ろうとしている。

ワニの移動速度は地上と水中では異なる。
図に示されているxメートル上流にある対岸のP地点へ向けてワニが泳いだ場合,獲物に到達するまでの移動時間が最小となる。
この所要時間は,以下の等式によって求められる。(引用者注:時間の単位は特に定める必要がないので省略し,無名数とする)

T(x) = 5*sqrt(36+x^2)+4*(20-x)

(a) (i) ワニが地上を移動しなかった場合の所要時間を求めよ。
    (ii) ワニの泳いだ距離が最小だった場合の所要時間を求めよ。
(b) これらの両極端の2つの選択肢の中間に,所要時間を最小化するxの値が1つある。そのxの値を求めよ。

この問題を R を用いて解く。
まず,T(x) の図を描いておこう(0 ≦ x ≦ 20)
> T = function(x) 5*sqrt(36+x^2)+4*(20-x)
> x = seq(0, 20, by=0.01)
> y = T(x)
> plot(x, y, type="l")


(a) (i) 「ワニが地上を移動しなかった場合」というのは,川を斜めに渡ったということ。すなわち,x = 20 のときの T(x) が答え。
> T(0)
[1] 110
答え 110

(a) (ii)  「ワニの泳いだ距離が最小だった場合」というのは,川を垂直に渡ったということ。すなわち,x = 0 のときの T(x) が答え。
> T(20)
[1] 104.4031
答え 104.4031

(b) T(x) が最小になるときの x を求めるということは,T(x) の微分係数が 0 になるときの x を求めるということ。
> g = deriv(~5*sqrt(36+x^2)+4*(20-x), "x", function.arg="x")
> g # 微分係数の関数
function (x)
{
    .expr2 <- 36 + x^2
    .value <- 5 * sqrt(.expr2) + 4 * (20 - x)
    .grad <- array(0, c(length(.value), 1L), list(NULL, c("x")))
    .grad[, "x"] <- 5 * (0.5 * (2 * x * .expr2^-0.5)) - 4
    attr(.value, "gradient") <- .grad
    .value
}
微分係数のグラフを描いておこう。
> plot(x, attr(g(x), "gradient"))
> plot(x, attr(g(x), "gradient"), type="l")
> abline(h=0, v=8, col="red", lty=2) # 結果論ではあるが x = 8 のとき,微分係数が 0 になることを図示しておく


関数の最小値を求めるのは optimize 関数が使える

> optimize(g, lower=0, upper=20, tol = .Machine$double.eps) # deriv 関数の戻り値 g を与える場合
$minimum
[1] 8

$objective
[1] 98
attr(,"gradient")
                 x
[1,] -5.039269e-08

または

> optimize(T, lower=0, upper=20, tol=.Machine$double.eps) # もとの関数を与える場合
$minimum
[1] 8

$objective
[1] 98

いずれにせよ $minimum = 8 が解である(最短時間は $objective = 98)。

なお,deriv 関数が返す導関数 5 * (0.5 * (2 * x * (36 + x^2)^-0.5)) - 4 が 0 になる値は,uniroot でも求められる。

> h = function(x) 5 * (0.5 * (2 * x * (36 + x^2)^-0.5)) - 4
> uniroot(h, lower=0, upper=20, tol=.Machine$double.eps)
$root
[1] 8

$f.root
[1] 0

$iter
[1] 8

$init.it
[1] NA

$estim.prec
[1] 2.993504e-05

$root = 8 が解である(そのとき,$f.root = 0 となる)

> h(8)
[1] 0

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

左右に行ったり来たり

2017年03月28日 | ブログラミング

左右に行ったり来たり

締め切りが 2017/03/28 10:00 AM なので,その 1 分後に投稿されるように予約

設問

一列に n 個のマスが並んでおり、各マスには 1~(n-1) のいずれかの数字が書かれています。
この一列のマスに対して、書かれている数字の数だけ左右に移動します。
このとき、進む方向は「左」「右」を交互に繰り返します。
最初、左端から右向きにスタートして、右端のマスに到達するような数字の配置を考えます。
なお、右端のマスに到達した時点で終了するため、右端のマスは0とします。
また、左端のマスより左、右端のマスより右には移動できないため、そのような数字の配置はできないものとします。
例えば、以下のマスのように配置されていると、図のように移動します。



このように、右端に到達できる数字の配置のうち、すべてのマスでちょうど一度ずつ止まるものが何通りあるかを求めてください。
例えば、n = 6 の場合は、上記の左図の他に右図のようなパターンがあり、全部で5通りです。
なお、n は12以下の整数とします。

【入出力サンプル】
標準入力
6

標準出力
5

===============================

小さな n の場合の結果を計算する,以下のような簡単なプログラムを書き,既知の数列かどうか検索する。

check = function(n, nm1, p) {
    location = integer(nm1)
    i = 1
    for (j in 1:nm1) {
        location[i] = p[i]
        i = i+location[i]*ifelse(j %% 2 == 1, 1, -1)
        if (i < 1 || i > n) {
            return(FALSE)
        } else if (i == n) {
            return(all(location != 0))
        } else if (location[i] != 0) {
            return(FALSE)
        }
    }
    return(FALSE)
}
f = function(n) {
    nm1 = n - 1
    p = rep(1, nm1)
    p[1] = 2
    ptr = nm1
    count = 0
    repeat {
        count = count+check(n, nm1, p)
        p[ptr] = p[ptr] + 1
        if (p[ptr] == n) {
            temp = ptr
            repeat {
                temp = temp - 1
                if (temp == 0) {
                    break
                } else if (p[temp] < nm1) {
                    p[temp] = p[temp]+1
                    p[(temp+1):nm1] = 1
                    break
                }
            }
            if (temp == 0 || p[1] == nm1) {
                break
            }
        }
    }
    count
}

n が奇数の場合は F(n) = 0

R だと,n = 8 はかなり時間が掛かる。
Java でも,n = 10 までで,n = 12 は止めておこうという気になる。

n = 2, 4, 6, 8, 10 に対して,F(n) = 1, 5, 61, 1385

結果として,既知の数列である。
https://oeis.org/search?q=1%2C5%2C61%2C1385&language=japanese&go=%E6%A4%9C%E7%B4%A2

a(n) = Sum_{k = 0..2*n} Sum_{j = 0..k} (-1)^(n-j) *binomial(2*n+1,k-j)*(j+1/2)^(2*n)

これを取り込んだプログラム

f = function(n) {
    s = 0
    if (n%%2 == 0) {
        n = n/2-1
        for (k in 0:(2 * n)) {
            for (j in 0:k) {
                s = s + (-1)^(n - j) * choose(2 * n + 1, k - j) * (j + 0.5)^(2 * n)
            }
        }
    }
    options(scipen = 100)
    cat(s)
}


f(4) # 1
f(5) # 0
f(6) # 5
f(8) # 61
f(10) # 1385
f(12) # 50521
f(14) # 2702765
f(16) # 199360981 であるが,計算誤差のため 199360916 になってしまう

gmp を使えばよい。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

縦線と横線でマス目を塗る

2017年03月22日 | ブログラミング

縦線と横線でマス目を塗る

締め切りが 2017/03/22 10:00 AM なので,その 1 分後に投稿されるように予約

【概要】

マス目をルールに従って黒く塗っていきます。
黒く塗られたマス目の数を数えてください。

【詳細】

マス目を塗る方法は、以下の二通りあります(以下、座標系はyが大きい方が上、xが大きい方が右です):
方向を表す記号     状況
V     下から上に塗る(y座標が大きい方向に塗りすすめる)
H     左から右に塗る(x座標が大きい方向に塗りすすめる)

【入出力】

入力は
H,1,2,8 V,4,1,8 H,1,7,5 V,7,7,2
のようになっています。

塗り方が、空白区切りで並んでいます。
塗り方は、「方向を表す記号」「塗りはじめのマスのx座標」「塗りはじめのマスのy座標」「マスの数」が、コンマ区切りで並んでいます。

出力は、
21
のような感じです。
塗られている面積を10進数で普通に出力してください。

【例】
入力                出力
H,1,2,8 V,4,1,8 H,1,7,5 V,7,7,2     21     



V,3,9,4 H,0,4,13 H,4,10,2 V,8,2,11     29     



V,8,7,2 H,8,10,6 V,9,9,7 H,10,14,4 H,11,0,3     21     



【補足】

    不正な入力に対処する必要はありません。
    x座標・y座標 は、いずれも 0以上 一千万 以下の整数です。
    長さ は、1以上 一千万 以下の整数です。
    「塗り方」の個数は 十 個を超えることはありません。

=========================

f = function(s) {
    s = matrix(unlist(strsplit(s, "[, ]")), 4)
    x = as.integer(s[2, ])
    y = as.integer(s[3, ])
    n = as.integer(s[4, ])
    t = s[1, ]
    L = length(t)
    for (i in 1:(L - 1)) {
        for (j in (i + 1):L) {
            if (t[i] == "H" && t[j] == "H") {
                if (y[i] == y[j] && (x[j] >= x[i] || x[i] >= x[j] || (x[i] >= x[j] && x[j]+n[j] >= x[i]+n[i]))) {
                    x[i] = min(x[i], x[j])
                    n[i] = max(x[i]+n[i], x[j]+n[j])-x[i]
                    n[j] = 0
                    t[j] = ""
                }
            } else if (t[i] == "V" && t[j] == "V") {
                if (x[i] == x[j] && (y[j] >= y[i] || y[i] >= y[j] || (y[i] >= y[j] && y[j]+n[j] >= y[i]+n[i]))) {
                    y[i] = min(y[i], y[j])
                    n[i] = max(y[i]+n[i], y[j]+n[j])-y[i]
                    n[j] = 0
                    t[j] = ""
                }
            }
        }
    }
    count = sum(n)
    for (i in 1:(L - 1)) {
        for (j in (i + 1):L) {
            if (t[i] == "H" && t[j] == "V") {
                count = count - (x[j] >= x[i] && x[i] + n[i]-1 >= x[j] && y[i] >= y[j] &&  y[j] + n[j]-1 >= y[i])
            } else if (t[i] == "V" && t[j] == "H") {
                count = count - (x[i] >= x[j] && x[j] + n[j]-1 >= x[i] && y[j] >= y[i] && y[i] + n[i]-1 >= y[j])
            }
        }
    }
    cat(count)
}
f(readLines(file("stdin", "r")))



コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

幅優先の二分木を深さ優先探索

2017年03月21日 | ブログラミング

幅優先の二分木を深さ優先探索

締め切りが 2017/03/21 10:00 AM なので,その 1 分後に投稿されるように予約

設問

下図のように、ノードが左から順に埋まっている二分木を考えます。
この二分木に対し、根元の要素を「1」とし、幅優先で順番に番号を付与していきます。
(ノードの数が10個の場合は左図のような番号が付与されます。)

ノードの番号と探索順



この二分木に対して、深さ優先探索を行います。
深さ優先探索では、左から順にもっとも深くなるまで進み、その後はバックトラックを行いながら順に探索します。
例えば、左図の場合は、右図のような順番に探索を行います。

m 個の要素が存在する二分木について、n 番目に探索したノードの番号を求めます。
例えば、m = 10, n = 6 のとき 5, m = 10, n = 8 のとき 3 となります。

標準入力から m, n がスペース区切りで与えられるとき、n番目に探索したノードの番号を標準出力に出力してください。
(m, n は m ≧ n を満たす整数とし、m は最大で3000とします)

【入出力サンプル】
標準入力
10 6

標準出力
5

=====================================================================

f = function(m, n) {
    tree = matrix(0, m, 3)
    for (i in seq_len(m)) {
        tree[i,] = c(i, ifelse(2*i <= m, 2*i, 0), ifelse(2*i+1 <= m, 2*i+1, 0)) # 値,左の子へのポインター,右の子へのポインター
    }
    node = count = 1
    repeat {
        if (count == n) { # 見つかった
            return(tree[node, 1]) # 値を返す
        }
        if (tree[node, 2] != 0) { # 左の子へ
            nxt = tree[node, 2]
            tree[node, 2] = 0 # 子へのポインターを潰しておく
            count = count+1
        } else if (tree[node, 3] != 0) { # 右の子へ
            nxt = tree[node, 3]
            tree[node, 3] = 0 # 子へのポインターを潰しておく
            count = count+1
        } else { # 終端ならば,子を持つ先祖までさかのぼる
            nxt = node
            repeat {
                nxt = nxt %/% 2
                if (tree[nxt, 2] != 0 || tree[nxt, 3] != 0) break
            }
        }
        node = nxt
    }
}
f(10, 6) # 5
f(100, 43) # 21
f(543, 345) # 410
f(1234, 987) # 896
f(3000, 2999) # 2046

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

複数の折れ線グラフを描くときの色指定法

2017年03月21日 | ブログラミング

中澤さんの書いた記事にある折れ線グラフが,ちょっと見づらいのでいくつか試してみた

中澤さんのプログラムは以下のとおり

# revised version of
# http://minato.sip21c.org/demography-special/asfrworld.R
# minatonakazawa@gmail.com, 20 March 2017
# requires wpp2015 package
library(wpp2015)
data(percentASFR)
worldfertil wfnow z matplot(z, type="l", lty=1, col=topo.colors(201),
 axes=FALSE, ylab="ASFR2010-2015", main="World Fertilities 2010-2015")
axis(1, 1:7, rownames(z))
axis(2, 0:5*10, 0:5*10)

添付図の左上がオリジナルで,col=topo.colors(201) によるもの。

その他の代替案として,いくつか

col=topo.colors(201, alpha=0.5)
col=topo.colors(201, alpha=0.2)
col=terrain.colors(201, alpha=0.2)
col=heat.colors(201, alpha=0.2)
col="#55000022"

alpha を採用することと,単純なモノクロを選択することが効果的と思われる。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

できる人のおちんぎんあっぷ

2017年03月16日 | ブログラミング

できる人のおちんぎんあっぷ

締め切りが 2017/03/16 10:00 AM なので,その 1 分後に投稿されるように予約

ここは株式会社 人月査定。
人月査定では、開発者の離職が課題になっていました。
調査してみると、優秀な開発者から順に離職していることがわかりました。
その原因を調査すると、どうやら多くの成果物を生み出す優秀な開発者とほとんど成果物を生み出していない開発者の給与が全く同じであるためでした。
そこで人月査定では成果主義による評価制度を導入することを決定しました。
社名も株式会社 ステップ数見積もり に変更です。
こうして社内特命プロジェクト「おちんぎんあっぷ大作戦」が始動しました。
「ステップ数見積もり社」ではバージョン管理ツールKit(きっと)でプログラムの履歴管理をしています。
Kitの履歴をもとに「どの開発者が何%の成果物を仕上げているか?」を調べてください。
Kitの履歴は1ファイル1行単位で残ります。
1つの履歴を保存することをコミットと呼びます。
コミット履歴は20件しか残りません。仕様です。

標準入力
Kitの履歴が標準入力されます
・コミットした開発者名、ファイル名がカンマ区切りで入力されます
・コミットした開発者名は [a-z] の文字列で構成されます
・コミットした開発者名は 3-8 文字です
・開発対象は「hoge.rb hige.rb hage.rb hoo.rb bar.rb」の5ファイルです
・入力データは20件

例(サンプルのため入力が3件ですが、実際は20件入力されます)
horiuchi,hoge.rb
hironaka,hige.rb
kondo,hoo.rb

標準出力
・出力形式は コミットした開発者名 + 半角コロン(:) + コミットしたファイルのパーセンテージ + 半角パーセント(%) です
・パーセンテージは小数点以下を切り捨ててください

例(標準入力の説明の入力が行われた場合の出力結果)

hironaka:33%
horiuchi:33%
kondo:33%

その他の仕様

・標準入力の末尾には改行があります
・標準出力の末尾に改行をつけてください
・同じファイルを複数の開発者が更新した場合、最後にファイルを更新した人がその成果物を仕上げた人としてください
 例えば hoge.rb を tanaka さん、 suzuki さん、 sato さんの順番で更新していたら
 hoge.rb の開発者は sato さんとして集計します
・標準入力の後半の行に出たほうが後から更新しているものとします
・標準入力の仕様で説明した内容以外の入力は行われません(不正入力に対するチェックは不要)

======================================================

素直に書くしかない

f = function(S) {
    m = sapply(S, function(s) unlist(strsplit(s, ",")))
    n = ncol(m)
    PROG = sort(unique(m[2,]))
    nProg = length(PROG)
    who = integer(nProg)
    for (j in seq_along(PROG)) {
        prog = PROG[j]
        for (i in seq_len(n)) {
            if (m[2, i] == prog) {
                who[j] = m[1, i]
            }
        }
    }
    who = table(who)
    invisible(mapply(function(x, y) cat(sprintf("%s:%i%%\n", x, y)), names(who), floor(who/nProg*100)))
}
f(readLines(file("stdin", "r")))

[input]            [output]
mashimo,hoge.rb    ara:40%
hakuryu,bar.rb     hakuryu:20%
hironaka,hoge.rb   horiuchi:20%    
kondo,hige.rb      kondo:20%
kuga,hige.rb
kondo,hoo.rb
hano,hige.rb
hamakawa,hoge.rb
kuga,hige.rb
hakuryu,hage.rb
hironaka,bar.rb
horiuchi,bar.rb
hakuryu,hoo.rb
ara,bar.rb
horiuchi,hage.rb
hironaka,hoo.rb
kuga,hoge.rb
hakuryu,hoo.rb
ara,hoge.rb
kondo,hige.rb

[input]            [output]
hironaka,hoge.rb   ara:20%
horiuchi,hoo.rb    hada:20%
mashimo,hoo.rb     hakuryu:20%
hano,hige.rb       hamakawa:20%
hironaka,hage.rb   kondo:20%
hada,hoo.rb
hano,hige.rb
ara,hoge.rb
kondo,hige.rb
hamakawa,bar.rb
hakuryu,hage.rb
kondo,hige.rb
mashimo,hoo.rb
hamakawa,hoo.rb
kondo,hoge.rb
kuga,hoo.rb
ara,hige.rb
hamakawa,bar.rb
hada,hoge.rb
kondo,hoo.rb

[input]            [output]
hakuryu,hoo.rb     hamakawa:60%
hironaka,bar.rb    hironaka:40%
hakuryu,hoo.rb
hakuryu,hige.rb
hamakawa,hage.rb
hironaka,bar.rb
hironaka,hige.rb
hironaka,hige.rb
hironaka,hige.rb
hakuryu,bar.rb
hano,hoo.rb
hamakawa,hige.rb
kuga,bar.rb
kuga,hige.rb
kuga,hoo.rb
hamakawa,hoge.rb
hironaka,hige.rb
hamakawa,hoo.rb
ara,bar.rb
hironaka,bar.rb




コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

放物線とマス目の関係

2017年03月15日 | ブログラミング

放物線とマス目の関係

締め切りが 2017/03/15 10:00 AM なので,その 1 分後に投稿されるように予約

【概要】

放物線の方程式とマス目を指定するので、位置関係がどうなっているのかを計算してください。


【詳細】

放物線とマス目の位置関係は下表の5種類があります(以下、y 座標が大きい方向が上、x座標が大きい方向が右、になります):
記号     状況     例


U     完全に放物線より上にある     


u     マス目の内部は放物線より上にあるが、マス目と放物線に共有点がある。     


S     マス目を放物線が分断し、放物線より上にも下にも 0 より大きな面積がある     


L     完全に放物線より下にある     


l     マス目の内部は放物線より下にあるが、マス目と放物線に共有点がある。     



【入出力】

入力は
1 0 0 0 -3
のようになっています。
空白区切りになっていて、順に a, b, c, X, Y です。

a, b, c は、放物線
y=ax2+bx+c
の係数です。

X, Y はマス目の左下隅の座標です。マス目の大きさは、 1×1 です。
つまり、マス目の領域は
(x,y) = { |x,y| X≦x≦X+1, Y≦y≦Y+1 }
です。

出力は、
L
のような感じです。
U,u,S,L,lのいずれかを出力してください。

【例】
入力     出力
1 0 0 0 -3     L
1 0 -46 7 4     S

【補足】

    不正な入力に対処する必要はありません。
    a は、-100000 以上 100000 以下のゼロではない整数です。
    b, c, X, Y は、-100000 以上 100000 以下の整数です。
    分断される場合に出力されるSは、大文字です。ご注意ください。

==========================================================

f = function(par) {
    g = function(x) (a*x+b)*x+c
    par = as.integer(unlist(strsplit(par, " ")))
    a = par[1]
    b = par[2]
    c = par[3]
    x = par[4]
    y = par[5]
    x1 = x+1
    y1 = y+1
    top.bottom = c-b^2/4/a
    center = -b/2/a
    if (a > 0) {
        if (x >= center) {
            if (y > g(x1)) {
                ans = "U"
            } else if (y == g(x1)) {
                ans = "u"
            } else if (y1 == g(x)) {
                ans = "l"
            } else if (g(x) > y1) {
                ans = "L"
            } else {
                ans = "S"
            }
        } else if (center >= x1) {
            if (y > g(x)) {
                ans = "U"
            } else if (y == g(x)) {
                ans = "u"
            } else if (y1 == g(x1)) {
                ans = "l"
            } else if (g(x1) > y1) {
                ans = "L"
            } else {
                ans = "S"
            }
        } else {
            if (y > g(x) && y > g(x1)) {
                ans = "U"
            } else if (y == g(x) || y == g(x1)) {
                ans = "u"
            } else if (y1 == top.bottom) {
                ans = "l"
            } else if (top.bottom > y1) {
                ans = "L"
            } else {
                ans = "S"
            }
        }        
    } else if (a < 0) {
        if (x >= center) {
            if (y > g(x)) {
                ans = "U"
            } else if (y == g(x)) {
                ans = "u"
            } else if (y1 == g(x1)) {
                ans = "l"
            } else if (g(x1) > y1) {
                ans = "L"
            } else {
                ans = "S"
            }
        } else if (center >= x1) {
            if (y > g(x1)) {
                ans = "U"
            } else if (y == g(x1)) {
                ans = "u"
            } else if (y1 == g(x)) {
                ans = "l"
            } else if (g(x) > y1) {
                ans = "L"
            } else {
                ans = "S"
            }
        } else {
            if (y > top.bottom) {
                ans = "U"
            } else if (y == top.bottom) {
                ans = "u"
            } else if (g(x) > y1 && g(x1) > y1) {
                ans = "L"
            } else if (y1 == g(x) || y1 == g(x1)) {
                ans = "l"
            } else {
                ans = "S"
            }
        }
    }
    cat(ans)
}

f("-200 5160 -33275 12 6") # S
f("8 -8 2 0 -1") # l
f("110 -198 90 0 0") # S
f("40 -40 10 0 1") # S
f("10 -5 0 0 2") # S
f("10 -10 0 0 2") # U
f("-8 8 -2 0 1") # U
f("-3 7 4 -2 -6") # u
f("1 -10 25 3 4") # u
f("-200 5160 -33275 12 6") # S
f("4 -4 1 0 0") # S
f("10 -18 9 0 0") # S
f("-1 3 0 1 1") # l


コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

HTMLをスクレイプしてCSVに変換

2017年03月15日 | ブログラミング

HTMLをスクレイプしてCSVに変換

締め切りが 2017/03/15 10:00 AM なので,その 1 分後に投稿されるように予約

設問

Webページに含まれるテーブルの中身をスクレイプして、Excelなどで集計がしやすいようCSVファイルに変換する。
定期的に行う必要があるため、手作業で行うのではなく自動化したい。

求められるプログラムの仕様は、以下の通り。


    標準入力から、HTML準拠(Wikipedia参照)のテキストデータが送られる
    入力されるHTMLはフォーマットが正しい、具体的にはW3Cの構文検証サイトでエラーがないことを前提とする
    また、HTMLはUTF-8でエンコーディングされ、多バイト文字は含まれないものとする
    HTMLには、tableタグが1つだけ含まれる(テーブルは単体である)
    テーブル内のセルはtr, th, tdタグで構成され、セルが連結されることはない
    HTML内のテーブルを表形式として抽出し、CSVフォーマット(Wikipedia参照)で標準出力に返すこと
    出力するCSVはコンマ「,」(U+002C) 区切りで、すべてのフィールドをダブルクォート「"」(U+0022)で囲むこと
    CSVのフィールドとして出力される文字列は、thまたはtdタグ内のテキストに限定すること
    thおよびtdタグの扱いは、CSV出力時において変える必要はない
    thおよびtdタグ内に、さらにタグが含まれる場合、タグ自体は除去し配下のテキストだけを抽出すること
    文字列フィールドは文字実体参照(Wikipedia参照)を用いてアンエスケープ処理をすること
    ただし、(大なり記号)、&(アンパサンド)のみの対応でよいものとする

【問題】
標準入力から、HTMLフォーマットのテキストが送られます。
このHTMLからtableタグで囲まれた領域をスクレイプし、CSVに変換した上で、その結果を標準出力に返してください。
なお、アンエスケープ処理も忘れずに。

=================================================

f = function(s) {
    ncol = sum(grepl("", s))
    s = gsub("", "", s)
    s = gsub("title", "", s)
    s = gsub("&", "&", s)
    s = gsub("<", "    s = gsub(">", ">", s)
    s = (s[s != ""])[-1] # テストシステムでは先頭に 1 個の NULL が付いてしまっている
    apply(matrix(s, byrow=TRUE, ncol=ncol), 1, function(x) cat('"', paste(x, collapse='","'), '"\n', sep=""))
}
cat(f(readLines(file("stdin", "r"))))

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ホワイトデーのお返しの個数

2017年03月14日 | ブログラミング

ホワイトデーのお返しの個数

締め切りが 2017/03/14 10:00 AM なので,その 1 分後に投稿されるように予約

この問題の挑戦終了日はホワイトデー。
ということで、バレンタインデーにもらったプレゼントにお返しを送ろうと思っています。

もらったプレゼントの金額はわからないので、もらった個数に対してお返しの個数を変えることにします。
・義務チョコの場合はもらった個数と同じ数
・義理チョコの場合はもらった個数の2倍の数
・本命チョコの場合はもらった個数の3倍の数
※もらった時点で「義務チョコ」か「義理チョコ」か「本命チョコ」かはわかるものとします。

バレンタインには合計 m 個のプレゼントをもらいました。
お返しに用意した個数が n 個のとき、もらったプレゼントの種類について、義務チョコ・義理チョコ・本命チョコの個数の組み合わせを考えます。

例えば、m = 5, n = 10 のとき、以下の3パターンがあります。
パターン    義務チョコ     義理チョコ     本命チョコ
(1)         0個         5個         0個
(2)         1個         3個         1個
(3)         2個         1個         2個

標準入力から m, n が与えられるとき、上記の組み合わせの数を標準出力に出力してください。
(m, n は m < n を満たす整数とし、n は最大で1,000,000とします)

【入出力サンプル】
標準入力
5 10

標準出力
3

---------------------------------

下図のように,m がある値をとるとき,n は m ≦ n ≦ 3m である。m = 13 のとき,水色で示した数列には規則性があるので,それをそのままコーディング



f = function(m, n) {
    a = 0
    if (n >= m && 2*m >= n) {
        a = floor((n-m)/2)+1
    } else if (n > 2*m && 3*m >= n) {
        a = floor((3*m-n)/2)+1
    }
    cat(a)
}
mn = scan(file("stdin", "r"))
f(mn[1], mn[2])

この問題は,なかなか面白い

・義務チョコの場合はもらった個数の m1 倍の数
・義理チョコの場合はもらった個数の m2 倍の数
・本命チョコの場合はもらった個数の m3 倍の数

などとして,m1, m2, m3 をいろいろ変えると,ちょっと変態チックに面白い

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

あしあと問題

2017年03月09日 | ブログラミング

あしあと問題

締め切りが 2017/03/09 10:00 AM なので,その 1 分後に投稿されるように予約が

仕様

9x9のマップの中を標準入力の内容に応じて移動します。
移動するとあしあとが残ります。
どのようなあしあとが残ったか標準出力してください。

標準入力

・「^v<>」のどれかの文字を組み合わせた文字列が入力されます
・ ^ は上に移動です
・ v は下に移動です
・ < は左に移動です
・ > は右に移動です



>>>>>>>>vvvvvvvv<<<<<<<<^^^^^^^^

標準出力

・9文字x9行の文字列を出力してください
・1行目1文字目の位置をスタート地点とします
・スタート地点は必ず「あしあと」が残ります
・同じ場所を2回通った場合は「あしあと」が残ります
・移動した場所は「Y」(半角大文字のワイ)を出力してください。鳥の足跡をイメージしています。
・移動しなかった場所は「x」(半角小文字のエックス)を出力してください

例(標準入力の説明の入力が行われた場合の出力結果)

YYYYYYYYY
YxxxxxxxY
YxxxxxxxY
YxxxxxxxY
YxxxxxxxY
YxxxxxxxY
YxxxxxxxY
YxxxxxxxY
YYYYYYYYY

その他の仕様

・標準入力の末尾には改行があります
・標準出力の末尾に改行をつけてください
・移動回数は最大で48回です
・標準入力の仕様で説明した内容以外の入力は行われません(不正入力に対するチェックは不要)
・9x9からはみ出す位置への移動をするような入力は行われません(不正入力に対するチェックは不要)

=============

f = function(S) {
    x = y = 1
    m = matrix("x", 9, 9)
    m[x, y] = "Y"
    S = unlist(strsplit(S, ""))    
    for (s in S) {
        if (s == ">") {
            x = x+1
        } else if (s == "<") # < は半角で
            x = x-1
        } else if (s == "v") {
            y = y+1
        } else if (s == "^") {
            y = y-1
        }
        m[x, y] = "Y"
    }
    for (i in 1:9) {
        cat(sprintf("%s\n", paste(m[,i], collapse="")))
    }
}
# f(readLines(file("stdin", "r")))

f(">>>>>>>>vvvvvvvv<<<<<<<<^^^^^^^^")
f(">>vv>>vv>>vv>>vv")
f(">>>><vvv")

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

「プライム・ペア」問題

2017年03月09日 | ブログラミング

「プライム・ペア」問題

締め切りが 2017/03/09 10:00 AM なので,その 1 分後に投稿されるように予約

設問

自然数 k に対し、1 から k までの自然数のうち k と互いに素なものの個数を F(k) と定義します。
(F(k) はオイラーのΦ関数とも呼ばれています。参考:オイラーのφ関数(Wikipedia))
例えば F(12)=4 です。1 から 12 のうち 12 と互いに素なのは 1, 5, 7, 11 の 4 つです。
標準入力から、自然数 n(1 ≦ n ≦ 105)が与えられます。
標準出力に F(n!) を 1000003 で割った余りを出力するプログラムを書いてください。
例えば n=10 のとき、F(10!)=F(3628800)=829440 です。
同様に、F(20!) を 1000003 で割った値は 961998 です。

===================================

F = function(n) {
    mx = n # n 以下の素数リストを prime に作る
    tbl = 1:mx
    tbl[1] = 0
    for (i in 2:floor(sqrt(mx))) {
        if (tbl[i]) {
            mx2 = mx %/% i
            tbl[2:mx2*i] = 0
        }
    }
    prime = tbl[tbl > 0]
# F(n!) = n! * Π(1-1/prime[i]) = 1*2*3*…*n × (prime[1]-1)/prime[1] × (prime[2-1)/prime[2] …
# F(20!) = 1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19*20 * (1-1/2)*(1-1/3)*(1-1/5)*(1-1/7)*(1-1/11)*(1-1/13)*(1-1/17)*(1-1/19)
#        = 1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19*20 * 1/2 * 2/3 * 4/5 * 6/7 * 10/11 * 12/13 * 16/17 * 18/19
#        = 1*4*6*8*9*10*12*14*15*16*18*20 * 1*2*4*6*10*12*16*18
#        = 416084687585280000
# 416084687585280000 %% 1000003 = 961998
# i = 1 ~ n までの積。ただし,i が素数の場合は「i-1」
    x = 1:n
    x[prime] = x[prime] - 1
    ans = 1
    for (a in x) { # 要素の掛算(ただし,毎回 1000003 の剰余を結果とする)
        ans = (ans*a) %% 1000003

    }
    cat(ans)
}
#F(scan(file("stdin", "r")))

F(10) # 829440
F(20) # 961998
F(30) # 845477
F(100) # 145380
F(431) # 945906
F(2017) # 272985
F(81645) # 603326
F(99100) # 799614

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

グループで乗るリフト

2017年03月07日 | ブログラミング

グループで乗るリフト

締め切りが 2017/03/07 10:00 AM なので,その 1 分後に投稿されるように予約

設問

m 人のグループがスキー場でリフトやゴンドラに乗ろうとしています。
このスキー場には n 人乗りのリフトやゴンドラがあります。
グループでまとまって移動するため、連続したリフトやゴンドラに乗ることにします。
グループのメンバーは区別せず、各リフトやゴンドラに乗る人数だけを考えるとき、
1回の移動における乗り方が何通りあるかを求めてください。
ただし、誰も乗らないリフトやゴンドラはないものとします。
また、m, n ともに 32以下の正の整数とします。

例えば、m = 5, n = 3 のとき、以下の13パターンがあります。
パターン     1台目     2台目     3台目     4台目     5台目
(1)     1人     1人     1人     1人     1人
(2)     1人     1人     1人     2人     -
(3)     1人     1人     2人     1人     -
(4)     1人     2人     1人     1人     -
(5)     2人     1人     1人     1人     -
(6)     1人     1人     3人     -     -
(7)     1人     3人     1人     -     -
(8)     3人     1人     1人     -     -
(9)     1人     2人     2人     -     -
(10)     2人     1人     2人     -     -
(11)     2人     2人     1人     -     -
(12)     2人     3人     -     -     -
(13)     3人     2人     -     -     -

【入出力サンプル】
標準入力
5 3

標準出力
13

=====================

下図のような規則性を持った二次元数列になる

各列のオレンジの部分以降の桝目の数値は,それ以前の n 個の桝目の和(広義のフィボナッチ数列) n = 2(C 列)を見ればわかる



f
 = function(m, n) {
    a = integer(m+1)
    a[1] = 1
    a[2:n] = 2^(0:(n-2))
    for (i in n:m+1) {
        a[i] = sum(a[i-n:1])
    }
    cat(a[m+1],"\n")
}
if (0) {
    mn = scan(file("stdin", "r"))
    f(mn[1], mn[2])
} else {
    f(5, 3) # 13
    f(6, 4) # 29
    f(10, 2) # 89
    f(25, 10) # 16646200
    f(32, 16) # 2147205120
}



コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

PVアクセスランキング にほんブログ村

PVアクセスランキング にほんブログ村