CodeIQ:各桁総和を求める問題に挑戦してみた

CodeIQに掲載されていた各桁総和の問題に挑戦してみました。公式の回答受付が終了したので自分なりの答えをまとめておきます。

ルールは、 各桁総和を取って1桁ならその値を返す、複数桁ならもう一度総和をとるというもの。

たとえば

123 → 1+2+3 → 6
9876 → 9+8+7+6 → 30 → 3+0 → 3

という具合。

こうなるように下記のコードの穴を埋めよというもの。

function yourCode() {
    var arr = [];
    for (var i = 1; i <= 9999; i ++) {
        arr.push(-------your codes goes here-------);
    }
    return arr;
}

LEVEL1 入力欄の文字数:175文字以内

  • 禁止文字:なし

とりあえず各iに対して答えがどうなるかチェック

1->1, 2->2, 3->3, 4->4, 5->5, 6->6, 7->7, 8->8, 9->9,
10->1, 11->2, 12->3, 13->4, 14->5, 15->6, 16->7, 17->8, 18->9, 19->1, 20->2...

といったように1-9の繰り返しだということがわかりました。

i を 9 で割った余りである i%9 は 1,2,3,4,5,6,7,8,0,… となります。(i-1)%9 は 0,1,2,3,4,5,6,7,8,… となるためこれらに 1 を足す (i-1)%9+1 は
1,2,3,4,5,6,7,8,9… となってくれますのでこれが答えとなります(9文字)。

さて、これでは芸がないし、各桁を足して値を求めていないので正攻法で行くコードを考えます。まずは与えられた数字を分割しなければなりませんが、これは正規表現を使えばOK。

任意の一文字ごとに分割する正規表現は/./となります。ドットが任意の一文字で両側のスラッシュはデリミタ(区切り文字)です。また、このままだと1回ヒットした時点で文字分割が止まってしまうので最後まで検索させるgを付加して/./gとしましょう。たとえば1234に対して、”1234″.match(/./g)としてやれば、[“1”, “2”, “3”, “4”]という配列が返ってきます。便利ですね。

そうして出来た各配列の隣同士の要素を足し込んでいける便利な関数 reduce() が Array 型にはあります。
Array.reduce(function(firstElem,secoundElem){retunrn firstElem + secoundElem}) という感じで使います。あとはこの値が本当に一桁になっているか確認し、なっていなかったらこれまでの処理を再帰的に行えばいいという具合です。ちょっと複雑でしたが以下のようなコードになりました。

//一応最初に書いたコード
+function(){for(a=i;a>9;a=(a+"").match(/./g).reduce(function(x,y){return x-(-y)}));return a;}()


//--------わかりやすく書くと-----------
(function(){
    for(a = i; a > 9;
        a = String(a).match(/./g).reduce(
            function(x,y){return parseInt(x)+parseInt(y)})
    );
    return a;
)()


//--------全然わかりやすくなってない気がしたので(笑)即時関数の中身をわかりやすく展開-------------
for(a=i; a>9;){ //a=iとして、a>9だったら処理の必要あり。そうでなければaが各桁の総和

    //各桁の数字を分解した配列。各要素はString型になっていることに注意。
    var eachNumber = String(a).match(/./g);  

    //各配列の隣同士の和を取ることを繰り返す。このとき各要素はStringだからparseする。
    eachNumber.reduce( function(x,y){ return parseInt(x)+parseInt(y); }
}

//forループから抜けたので値を返す。
reuturn a;

う~ん、なんか汚いけどまあここらへんにしておきましょう。

LEVEL2 入力欄の文字数:50文字以内

  • 禁止文字:1 2 3 4 5 6 7 8 9

以降、1-9の数列をひたすら吐き出す力技で解きます。まず1-9の数の使用を禁じられたのでhoge%9というように周期9の数列を作り出すためには何らかの方法で数字の9を作らなければなりません。そこで自分で書いてて引いてしまったんですが(i-“.”.length)%”………”.length+”.”.lengthというように9文字のStringのlengthを参照する形で数を産み出しました。うわぁ、気持ち悪い・・・。まあとりあえずこんな方針で幾つかコードを書いていったら29文字まで縮みました。

//答えのパターンいろいろ
(i-".".length)%".........".length+".".length
(i-!!i)%".........".length+!!i
(i-!!i)%(Date.length+!0+!0)+!0
(i-!0)%(Date.length+!0+!0)+!0
(i-!0)%[,,,,,,,,0].length+!0

ちょっとだけ書いておくと-!0はint型の-1になります。またi>0なので!iは常に0に、!!iは常に1になります。[,,,,,,,,,]は要素数9の配列なのでlengthは9を返します。lengthって文字数多いな…。

LEVEL3 入力欄の文字数:100文字以内

  • 禁止文字:1 2 3 4 5 6 7 8 9 0 , + % ? : this eval function Function Array join split repeat ‘ “

0も+も%も禁じてきました。なんか良い方法があると思いますがもうかったるいのでLEVEL2的解き方を禁止文字を排除して書きなおしていく方針にします。対象のコードは(i-!0)%[,,,,,,,,0].length+!0としましょう。

まず[,,,,,,,,0]ですが、0とカンマが使えないのでなんとかしてまた9を生み出す方法を考えましょう。ビット演算子を用いて(!!i<<!!i<<!!i<<!!i)-(-!!i)とやると9になります。

次に-!0と+!0ですが、前者は-true、後者は-(-true)としますか。でも-!!iと-(-!!i)の方が短いですね(1文字だけど…)。んであとは剰余演算子%を使わずベタに計算するだけです。

最終的にこんなかんじになりました

(i-!!i)-((!!i<<!!i<<!!i<<!!i)-(-!!i))*parseInt((i-!!i)/((!!i<<!!i<<!!i<<!!i)-(-!!i)))-(-!!i)

//ちょっと改良
(i-!!i)-(typeof(z)).length*parseInt((i-!!i)/(typeof(z)).length)-(-!!i)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください