質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.52%

  • JavaScript

    16382questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

「-3, -2, -1, 1, 2, 3」の6つの整数をランダムで得る「いい」方法

解決済

回答 21

投稿 編集

  • 評価
  • クリップ 4
  • VIEW 2,679

miu_ras

score 601

最初は「-3~3の整数を得る」という仕様でよく、こんな感じでした。※本当の最初のコードはコメントアウトの物でしたが、ごくまれに-4が出るというバグがあるので修正しました。

var v = (Math.floor(Math.random() * 7) - 3);
//var v = (Math.ceil(Math.random() * 7) - 4);


ただ、0は除外しようと思いここからコードを変更しようと思ったのですが、シンプルな方法が思いつきませんでした。

一応、以下の2案は思いついたのですが、

  • 候補の値を配列で持つ
  • 「1, 2, 3」をランダムで得た後、「+ or -」をランダムで得てかける

あまりエレガントではないと思い、採用を躊躇しています。
この程度の規則なら数学的なテクニックを駆使して導き出るのではないかと思っています。

「-3, -2, -1, 1, 2, 3」の6つの整数をランダムで得る「いい」方法、
これについて教えてください。よろしくお願いします。

補足追記

2017/02/04 12:00頃、質問内容を「数学的」から「いい方法」に変更しました。

私がもともと考えていた「数学的な方法」とは以下の条件です。

  • Math関数と算術演算子・ビット演算子だけを使う
  • Math.randomの使用は1回のみ
  • 条件分岐・配列(あるいは配列に類するもの)を使わない
  • 出来れば、関数・変数を使わない、1行で書けるコード

ただ、「数学的な方法」にこだわりすぎたり、他の選択肢を捨ててしまうのも違うなと思い、「いい方法」に変更しました。

最初に考えていた「数学的な方法」を満たす回答も引き続き歓迎しますが、同時にシンプルな方法や、意外な方法もお待ちしています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • think49

    2017/02/03 21:43

    「数学的なテクニック」と「エレガント」について具体的な指標を下さい。 私なりに数学的でエレガントなコードを書いてみましたが、miu_ras さんの求めるものになっているのか、自信がありません…。

    キャンセル

  • 退会済みユーザー

    2017/02/06 00:36

    こちらの質問が他のユーザから「問題・課題が含まれていない質問」という指摘を受けました
    teratailでは、漠然とした興味から票を募るような質問や、意見の主張をすることを目的とした投稿は推奨していません。
    「編集」ボタンから編集を行い、質問の意図や解決したい課題を明確に記述していただくと回答が得られやすくなります。

回答 21

+13

「数学的」という言葉の定義が不明なので確かな回答はできませんが、

var v = Math.floor(Math.random() * 6);
v -= (v < 3) ? 3 : 2;


または

const map = [-3, -2, -1, 1, 2, 3];
var v = map[Math.floor(Math.random() * 6)];

ちなみに、

var v = (Math.ceil(Math.random() * 7) - 4);


と書くと、v は -4, -3, -2, -1, 0, 1, 2, 3 のいずれかの整数となります。

なぜなら、0 <= Math.random() < 1である(※)ため、Math.random() が 0 を返したとき、

Math.ceil(0 * 7) - 4
    == 0 - 4
    == -4;


となるからです。

※ https://www.ecma-international.org/ecma-262/5.1/#sec-15.8.2.14

Returns a Number value with positive sign, greater than or equal to 0 but less than 1

正の符号で、0 以上 1 未満の数を返します

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 11:27

    ceilの件はそうですよね。質問を投稿して出勤途中に気付きました。誰も気づかないことを願っていたのですが、teratailでJavaScriptでは無理だったようですね。

    キャンセル

  • 2017/02/04 11:28

    回答も参考になりました。ありがとうございました

    キャンセル

+11

0-5をマッピングする式だけ書きますが

(x+4)%7-3


スマホからなので同じ回答見落としていたらごめんなさい。


追記。ちゃんとJSで書きます。

return (Math.floor(Math.random() * 6) + 4) % 7 - 3

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/10 09:55

    すみません。どういう意味ですか?
    全くわかりません。
    最低限、JavaScriptで何かしらの結果が得られるコードを書いていただきたいです。

    キャンセル

  • 2017/02/10 10:28 編集

    これ、きれいですね。というか巧い!

    キャンセル

  • 2017/02/10 10:32

    なるほどー

    キャンセル

  • 2017/02/10 10:39

    0 から 5 まで x が変化するとき、計算結果は 1, 2, 3, -3, -2, -1 となりますね。
    つまり 0 から 5 までの乱数を求めてこの計算式に当てはめれば目的の乱数が得られると。

    キャンセル

  • 2017/02/10 11:12

    ああ、個人的にはこれがベストですね。一般化してカリー化するなら n => x => (x => (x+n+1)%(2*n+1)-n )(Math.floor( Math.random() * 2 * n ))

    キャンセル

  • 2017/02/10 12:44

    miu_rasさん、失礼しました。コードの意図はZuishinさんの補足していただいたとおり、0〜5の乱数をこの式に突っ込めば-3〜-1,1〜3の乱数になるというものです。
    ただ、言われてJSで清書して気づいたのが、JSの乱数ってそういえば最初から整数でなく実数だったんですよね… 0.5とかの実数が登場するのが気持ち悪いと思ってひねり出した式だったのですが、もともと実数なのならそっちでもいいかとなったところです。

    整数で乱数を発生させる処理系だったらもっと鮮やかにきまったのですが。

    キャンセル

  • 2017/02/11 10:03

    なるほど。「xが0-5」だったのですね。
    シンプルでいいですね。余りをうまく使うのが鍵ですね。
    ありがとうございました

    キャンセル

checkベストアンサー

+5

var v = (Math.floor(Math.random() * 3) + 1) * ((Math.floor(Math.random() * 2) + 1) * 2 - 3);

random の仕様がわかっていませんでした。例題を見て 0 は出ないと思い込みました。書き直しました。

もう一つ。

var a = Math.floor(Math.random() * 6);
var v = a - 3 + Math.floor(a / 3);

もう一つ。

var v = Math.round((Math.floor(Math.random() * 6) - 2.5) * 1.1);

追記
三番目のものについて修正しました。
また、検証しました。
以下のコードにて

<!DOCTYPE html>
<html>
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script>
        $(function(){
            var data = {};
            for (var i = 0; i < 1000; i++) {
                var v = Math.round((Math.floor(Math.random() * 6) - 2.5) * 1.1);
                if (!(v in data)) {
                    data[v] = 0;
                }
                data[v]++;
            }
            for (var key in data) {
                $('table').append(`<tr><th>${key}</th><td>${data[key]}</td></tr>`);
            }
        });
    </script>
</head>
<body>
    <table>
        <tr>
            <th>数値</th><td>出現回数</td>
        </tr>
    </table>
</body>
</html>


次の結果となりました。

数値    出現回数
1    139
2    179
3    167
-1    177
-2    162
-3    176

0 は発生せず、仕様を満たしているようです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/03 09:37

    マイナスの理由を教えて下さい。

    キャンセル

  • 2017/02/03 09:54 編集

    一項目のMath.random()が0のときv=0では?
    ほぼありえないと思いますが。

    キャンセル

  • 2017/02/03 09:57 編集

    ozwk さんありがとうございます。了解しました。お題を見て仕様を誤解していたので書き直しました。

    キャンセル

  • 2017/02/04 11:19

    そうなんですよね。ceilだとマレにしか出ないバグがあるんですよね。質問を投稿して出勤途中に気付きました。誰も気づかないことを願っていたのですが、teratailでJavaScriptでは無理だったようですね。

    キャンセル

  • 2017/02/04 11:25

    私にとっての理想的な形は3つめです。ただ、3つめは「-2. -1, 0, 1, 2, 3」になっていました…。
    1つめ2つめも参考になりました。ありがとうございました

    キャンセル

  • 2017/02/04 20:18

    やっぱり頭の中だけで考えてもうまくいかないものですね。
    と言いつつ確かめてませんが、修正しました。

    キャンセル

  • 2017/02/05 09:42

    修正後の3つ目は、実行するとできているように見えますね。
    ただ、ごくまれに0になるバグ込みだと思います。

    キャンセル

  • 2017/02/05 11:06

    まだ 0 になりますか?

    Math.floor(Math.random() * 6)
    これが 0,1,2,3,4,5

    2.5 を引いて -2.5,-1.5,-0.5,0.5,1.5,2.5

    1.1 を乗じて
    -2.75,-1.65,-0.55,0.55,1.65,2.75

    四捨五入して
    -3,-2,-1,1,2,3

    こうなるつもりだったんですが。
    どこが悪いかわからないのでやはり後で実際に確かめます。

    キャンセル

  • 2017/02/05 11:20

    すみません。勘違いでした。問題ないと思います。
    とすると、randomが1回で四則演算とMathだけで完結しているのでかなりいいですね。

    キャンセル

  • 2017/02/05 21:36

    そうですね。検証してみましたが、うまく動きました。

    キャンセル

+5

「-3, -2, -1, 1, 2, 3」を配列として捉えて、ランダムに選ぶのは、ワリとやる手だと思います。
シンプルだと思いますが。

数学的な方法って、どんなものをイメージしていますか?

補足
シンプルな方法ということで、乱数の元は組み込まれた関数を使用するものとします。
すると乱数は連続する数値の範囲を指定することになります。

その前提で考えると、「-3, -2, -1, 1, 2, 3」を連続した数値に直すか、連続した順番で捉えるかの2択になるかと。

前者は多段な処理を行わなければならないのであまりシンプルな発想ではないと思います。
シンプルさで言えば、後者の配列として捉える方法が適解かと。

重要な追記
途中から、識者が指摘を諦めてますがw

Math.random() の取る範囲が、0以上1未満なので、一様性を確保しようとするとMath.floor()と組み合わせるのがセオリーなんですね。今回は勉強になりました。

Math.ceil() 使っているヒトォー、0 問題クリアできていませんよぉー。
0を有効数字として扱うと、一様ではなくなるので組み合わせとしては最悪ですね。

Math.round() はコツがいりますね。発生した乱数を0.5ずらしてやらなければ一様にならない。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/03 09:37

    十分にシンプルな方法があるのに質問するから大喜利みたいになってる。。。w面白いですね。

    キャンセル

  • 2017/02/03 12:23 編集

    撃沈しました!やはりte2jiさんのおっしゃるとおりなのかな。ありそうに思えるのでなんか悔しいですw;
    みごとに与太郎を演じてしまいました。

    キャンセル

  • 2017/02/03 12:52

    みんな発想が柔軟ですよね。私にはこんなにたくさんのやり方は思い浮かばないです。
    単純な結果を求めるアルゴリズムの質問は盛り上がって面白いです。

    キャンセル

  • 2017/02/03 22:38 編集

    私見ですが、この手法の問題は [-3, -2, -1, 1, 2, 3] を生成するアルゴリズムが掲示されていない事にあると考えています。
    任意の数値反意で乱数を得る事を考えた場合、動的に配列を生成するエレガントなコードが必要になりますが、私が書いたところではエレガントとは呼べないコードになってしまいました。
    ただ、te2jiさんは「決め打ちで良い」の前提で配列を用意する手法を提案されていると思いますし、miu_rasさんがどうお考えになっているかで回答の方向性が変わってくると思います。

    キャンセル

  • 2017/02/03 23:16

    たしかに、元の数列の生成アルゴリズム(というか選定理由?)が提示されると、アルゴリズムの拡張性を考え、適正回答は変わるかもしれないですね。
    ただ、連続性は今以上に無くなると思うので、やはり配列が有利だと思います。

    せっかく think49 さんがコメントくれたんで教えてほしいのですが、配列を生成するコストっていうのがよく理解できませんでした。べた書きがコストってりかいでただしいですか?

    キャンセル

  • 2017/02/03 23:27

    To: te2ji さん
    私の回答から createIntArray2 でページ検索してみて下さい。
    私の頭が固いだけかもしれませんが、3行もコードがあって洗練されていないと感じています。
    1行かつメソッドチェーン数をもっと減らしたいのです。

    キャンセル

  • 2017/02/03 23:46

    文字通り、配列を作っていたんですね。
    (関数の中身が理解できていなかったもので^^;)

    文章の方は理解できました!w

    キャンセル

  • 2017/02/04 09:56

    みなさんの回答を改めて確認して、気がついたことがあったので、追記しました。今回は勉強になりました。

    キャンセル

  • 2017/02/04 11:01

    私が最初にあげた配列を使う方法でやった方がいいということですね。ありがとうございました

    キャンセル

  • 2017/02/04 11:35

    配列作るコストを考えなくていいのであれば、配列最強かとw
    think49 さんの指摘通り、 [-3, -2, -1, 1, 2, 3] を生成するアルゴリズム次第かと思います。

    個人的には、katoy さんのが好きですね。

    キャンセル

  • 2017/02/04 11:52

    横から失礼します。
    質問では『「-3, -2, -1, 1, 2, 3」の6つの整数』と明確に要件を提示しているので、配列を使うなら単純に`var v = [-3, -2, -1, 1, 2, 3][Math.floor(Math.random() * 6)];`と1行でできます。おそらく配列の生成コストはかからないと思います。

    キャンセル

  • 2017/02/04 12:17

    今回、与えられた前提条件としては、配列の生成コストを考えないくていいので、配列が最強だと思ってますよ。そう書いてますし。

    ただ、「-3, -2, -1, 1, 2, 3」の6つの整数がなんらかのアルゴリズムで生成されている場合、話は変わります。

    わかりやすい例で言うと、「-3, -2, -1, 1, 2, 3」の6つの整数が、「1 から 6 までの整数を選び、それが 4 以上であれば 7 引く」ってアルゴリズムによる生成だったとすると、それをそのまま採用して、乱数発生後の数字を加工したほうが良いケースもあるかと。分かりやすいし。

    また、今回の質問を簡略化しているだけで、実際に使う数列は、「-3, -2, -1, 1, 2, 3」でない場合、その選択要件次第では、配列生成コストが大きくなる可能性もあります。

    結果、配列を生成するアルゴリズム次第じゃないかと。

    キャンセル

  • 2017/02/04 13:19

    te2ji さん

    すみません。名前を書き忘れたため誤解されてしまったようです。think49さんの「1行かつメソッドチェーン数をもっと減らしたいのです。」というコメントに対して反応したつもりでした。

    それと、

    > ただ、「-3, -2, -1, 1, 2, 3」の6つの整数がなんらかのアルゴリズムで生成されている場合、話は変わります。

    そうなると質問の趣旨から外れてしまうのではないでしょうか。「この程度の規則」とも書かれているので、質問に書かれている以外のアルゴリズムを持ち出すのは適当ではないと考えます。

    キャンセル

  • 2017/02/04 13:52

    書いているとおり、配列作るコストを考えなくていいのであれば、配列最強と思ってます。
    ただ、選択した数字群がなんらかのアルゴリズムに基づいているなら、それを選択したアルゴリズムをトレースするほうが良いケースもあると思います。提示されていないので、それが提示されれば、最適解が変わるかもしれないということです。
    think49 さんの 【この手法の問題は [-3, -2, -1, 1, 2, 3] を生成するアルゴリズムが掲示されていない事にあると考えています。】というコメントのとおりですね。

    ちょっと指摘内容が頭に入ってきません。なにかスレ違いがあるのだと思いますが。

    キャンセル

  • 2017/02/04 16:25

    繰り返しになってしまいますが、質問には『「-3, -2, -1, 1, 2, 3」の6つの整数』と明示されていて、その6つの整数をランダムで取得するにはどうするかというのが質問の趣旨であって、6つ(もしくはそれ以上)の数列を生成するアルゴリズムまで話を膨らませる必要はないと言いたかったのです。
    混乱させて申し訳ありません。私のコメントは無視してもらってもかまいません。

    キャンセル

  • 2017/02/04 18:50 編集

    To: catsforepaw さん
    命題の捉え方の問題だと思います。
    エレガントの定義がなかったので、私が考えるエレガントなコード(汎用性の高いコード)を書いてみました。
    「エレガント」に対するイメージは万人共通ではないので、質問者の要件によって答えが変わりますね、と。

    キャンセル

+5

2ビットと符号ビットで足りることに着目し、000100を排除すればいいことに気づきました。
1~6をランダムに生成し、下位2ビットを残し、3桁目ビットで全ビットそろえてXOR取ってます。

var v = ( x =>
  ( x & 0b11 ) ^ ( 0- ( ( x & 0b100 ) >> 2 ) )
)( Math.random() * 6 + 1 )
 追記

think49さんの「汎用性が高いコードがエレガント」という言葉と、raccyさんのカリー化に刺激を受けまして、一般化してみました。

var f =  n => 
  x => ( x =>
    x + (( x & 0b1000000000000000000000000000000) >> 30) - 0b1000000000000000000000000000000
  )( Math.floor( Math.random() * 2 * n + ( 0b1000000000000000000000000000000 - n ) ) );
var v = f(3)();


f(n)は[n, n-1, ... 1,-1, ... -(n-1), -n]の乱数を返す関数を返します。たぶん。
ただし、javascriptで扱える整数に上限があるため、nには上限があります。おそらく。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 15:55

    これは少し難しいのですが、かっこいいですね。
    「000と100を排除すればいい」この発想がすごいですね。
    勉強になります。ありがとうございました

    キャンセル

  • 2017/02/04 20:52

    ありがとうございます。かっこいいと言っていただけてとてもうれしいです。
    調子に乗って一般化してみました。
    でも実際、現場でこんな可読性の悪いコードを書いてたら怒られるんでしょうね。

    キャンセル

+4

$ node
> function f(x) { return (x - 2.5) + Math.sign(x - 2.5) * 0.5; }
undefined
> function rand() { return Math.floor(Math.random() * 6); }
undefined

>  [f(0), f(1), f(2), f(3), f(4), f(5)]
[ -3, -2, -1, 1, 2, 3 ]

>  [f(rand()), f(rand()), f(rand()), f(rand())]
[ 3, -2, 1, -1 ]
>  [f(rand()), f(rand()), f(rand()), f(rand())]
[ -3, 3, 1, 3 ]
>  [f(rand()), f(rand()), f(rand()), f(rand())]
[ -2, -2, -2, 1 ]
>  [f(rand()), f(rand()), f(rand()), f(rand())]
[ -1, 3, -3, 1 ]
>  [f(rand()), f(rand()), f(rand()), f(rand())]

数学的には、x に対する f(x) が次のようになる関数 f を作ればよいことになります。
x:     0  1  2 3  4  5
f(x): -3 -2 -1 1  2  3

f(x) = (x - 2.5) + sign(x - 2.5) * 0.5 は、その 1 つの例になります。
ここで sign(x)は 
x < 0 なら -1 を返す
0 < x なら 1  を返す
ような関数とします。

f を javascript (node.js) で書き、動作を試してみたのが↑です。

追記:
数学的なことをだらだらと述べます。

f(n) = m が n 個与えられたとき、それを満たす x の多項式はいくつも存在します。
一般的には n 個に対しては (n - 1) 次多項式で、それが可能です。
(2点なら、1次式で可能になる)

参考

ここでの質問の場合は、6つの f(x) = m があたえられているので 5 次多項式の答えが存在するはずです。

しかし、ここでの質問のケースでは、 ほとんど直線であり、その直線を途中でちょっとずらすだけで条件をみたすことができます。
ずらす操作を if での条件分岐で記載することも可能です。
普通の多項式はすべてなだらかなグラフになりますが、なだらかにならない式として 
|x| とか sgn(x) とか floor(x) とか ディリクレ関数 ... があります。

ここでは、 sgn(x) (x が正なら 1, 負なら -1, 0 なら 0 を返す) を利用することで、直線をずらす処理を数式で
表現してみたのです。
floor(x) をつかっても、直線をずらすことを表現できる気もします。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 12:15

    Math.signを使うのは数学的でかっこいいですね。
    ありがとうございました

    キャンセル

  • 2017/02/04 16:21

    …Math.signを三角関数と間違えていました。

    ようやく理解しました。
    「{}」を乱数のとりうるパターンとすると、
    「{0, 1, 2, 3, 4, 5} - 2.5」で
    {-2.5, -1.5, -0.5, 0.5, 1.5, 2.5}をつくり
    それをsignにかけその結果に0.5をかけて{-0.5, 0.5}にして加算。
    数学的でかっこいいですね。ありがとうございました

    キャンセル

  • 2017/02/04 20:38

    0.5ずらす発想が秀逸ですね。単純な四則演算だけで構成されている点も分かりやすくて良いと思いました。
    Simple is best!

    キャンセル

+3

無理やりやってみました。
引き直しなど条件分岐は行わず、
[0,5]の整数の一様分布に対してちゃんと均等な出現確率になるようになっています(多分)

function f(t){
    return Math.ceil(Math.abs(t-2.5))*Math.sign(t-2.5)
}

function g(){
    r = Math.floor(Math.random() * 6)
    return f(r)
}

console.log([0,1,2,3,4,5].map(f))

histogram=[0,0,0,0,0,0,0]
for(i=0;i<1000;i++){
    histogram[g()+3] ++
}
console.log(histogram)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 11:11

    私が最初に使っていた言葉の「数学的な考え」はこんな感じです。
    ただそこにこだわりすぎると複雑になってしまうようですね。
    勉強になります。ありがとうございました

    キャンセル

+3

整数の商と剰余を使えばできますね。

出来れば、関数・変数を使わない、1行で書けるコード

には反しますが...

function randomValue() {
  return (function(x) {
    return (x % 3 + 1) * (Math.floor(x / 3) * 2 - 1);
  })(Math.floor(Math.random() * 6));
}

もうひとつ、全然エレガントではない方法。
結局、[0, 1, 2, 3, 4, 5] => [-3, -2, -1, 1, 2, 3] の単射が欲しいわけです。
長さnの数値配列は、n-1次多項式で表現できます。
5次式だと大変なので、[0, 1, 2, 3] => [-2, -1, 1, 2] で考えます。
(x, y) = (0, -2), (1, -1), (2, 1), (3, 2)
の4点を通る3次関数 y = ax^3 + bx^2 + cx + d の係数は、線形代数だけで求められ、

y = -1/3x^3 + 3/2x^2 - 1/6x - 2

となります。

function randomPoly() {
  return (function(x) {
    return -1/3*x*x*x + 3/2*x*x - 1/6*x - 2;
  })(Math.floor(Math.random() * 4));
}

いかがでしょうか?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/05 11:32

    1つめは、意外なことに「% 3」を使うアプローチはこれが初のようです。
    2つめは、実用性は低いかもしれませんが、アプローチが面白いですね。
    ありがとうございました

    キャンセル

+2

ゼロが出たら再発行
While( (v = (Math.ceil(Math.random() * 7) - 4) === 0 ){}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 11:12

    ループですかー。ありがとうございました

    キャンセル

+2

数学的なテクニックっぽい計算式

var v = Math.round(Math.sin((Math.floor(Math.random() * 6) - 2.5) * 36 * Math.PI / 180) * 3);


エレガントにはほど遠いですね。パフォーマンスも最悪だと思います。
配列を使うのが一番シンプルで無難かと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 11:33

    むずいですね…。でも私が最初に求めていた要件を満たすロジックです。
    勉強になります。ありがとうございました

    キャンセル

  • 2017/02/04 11:43

    おそらく質問者さんはrandomを2回使うことがエレガントではないと考えたのだろうと思い、1回で済ませる方法を考えたのですが、線形なスケーリングでは無理だと判っていたので、前半は急で後半はなだらかになるような関数に当てはめれば良かろうと思いつき、真っ先に浮かんだのがsin関数でした。

    キャンセル

+2

miu_ras さんご本人が think49 さんの回答に対してのコメントの中でしている式です。

var v = (Math.floor(Math.random() * 6) - 3) || 3;

非常にシンプルで面白いと思います。分かりやすいし。
コメントに埋もれさせておくのがもったいなかったので、パクリ回答してみましたw

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/05 20:29

    書き込もうとしたらすでにでてたので+いれときました

    キャンセル

+1

「-3≦x≦3(端を含むかはそこまで気にしなくてもいい)の一様乱数を生成して、出た小数を無限大に丸める」という方法も考えられます。

ただ、運悪く(?)ちょうど0が出たときはどうするかは、ちょっと検討が必要かもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/03 09:22

    これ、面白いですね。実用の範囲で一様かと。
    それでも、私は3回連続ちょうど0を引く自信があります!

    キャンセル

  • 2017/02/04 11:03

    「無限大に丸める」というのはコードとしてはどうなるのでしょうか?

    キャンセル

+1

数学的かはわかりませんが、その他の方法として、、、

// 1〜6を取得して3より大きければ7を引く
var v = Math.ceil(Math.random() * 6);
if(v > 3) {
    v -= 7
}

// 1〜3を取得して、ランダムに符号を変える
var v = Math.ceil(Math.random() * 3);
if(Math.random() < 0.5) {
    v *= -1
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 11:08

    なるほど。条件分岐を使っている点が少しに来なりますが、
    結構シンプルでいいですね。ありがとうございました

    キャンセル

+1

 数学的にエレガントとは

「miu_ras さんがエレガントと思うコードの定義」が不明なので、私がエレガントと思うコードの定義を書きます。

(1) [-3,-2,-1,0,1,2,3] からランダムに値を得てから 0 を例外処理するコードはエレガントではない

0 を除外するコードは2種類考えられます。

  • 0 が返された場合、1 を返す
  • 0 が返された場合、もう一度、[-3,-2,-1,0,1,2,3] からランダムに値を得る(0 が返されなくなるまで繰り返す)

前者は 1 が返される確率が上がる為、エレガントではありません。
後者は 0 が返された場合の処理コストが上がる為、エレガントではありません。

(2) 配列からランダムに値を得る方法はエレガントではない

[-3,-2,-1,0,1,2,3] からランダムに値を得る方法はシンプルですが、配列を生成するコストがかかるのでエレガントではありません。

 配列からランダムに値を得るコード

[1,2,3] を元に [-3,-2,-1,1,2,3] を生成するコード。

function createIntArray1 (minInt, maxInt) {
  var array = [...Array(maxInt + 1).keys()].slice(minInt);

  return array.slice().reverse().map(value => value * -1).concat(array);
}

function createRandomInt1 (minInt, maxInt) {
  var array = [...Array(maxInt + 1).keys()].slice(minInt);

  array = array.slice().reverse().map(value => value * -1).concat(array);
  return array[Math.floor(Math.random() * array.length)];
}

console.log(JSON.stringify(createIntArray1(1, 3)));  // [-3,-2,-1,1,2,3]
console.log(createRandomInt1(1, 3));
console.log(createRandomInt1(1, 3));
console.log(createRandomInt1(1, 3));
console.log(createRandomInt1(1, 3));
console.log(createRandomInt1(1, 3));
console.log(createRandomInt1(1, 3));
console.log(createRandomInt1(1, 3));
console.log(createRandomInt1(1, 3));
console.log(createRandomInt1(1, 3));
console.log(createRandomInt1(1, 3));

[0,1,2,3,4,5,6,7] を元に [-3,-2,-1,1,2,3] を生成するコード。

function createIntArray2 (minInt, maxInt) {
  var halfLength = maxInt - minInt + 1,
      array = [...Array(halfLength * 2 + 1).keys()].map(value => value - halfLength);

  return array.splice(halfLength, 1), array;
}

function createRandomInt2 (minInt, maxInt) {
  var halfLength = maxInt - minInt + 1,
      array = [...Array(halfLength * 2 + 1).keys()].map(value => value - halfLength);

  return array.splice(halfLength, 1), array[Math.floor(Math.random() * array.length)];
}

console.log(JSON.stringify(createIntArray2(1, 3)));  // [-3,-2,-1,1,2,3]
console.log(createRandomInt2(1, 3));
console.log(createRandomInt2(1, 3));
console.log(createRandomInt2(1, 3));
console.log(createRandomInt2(1, 3));
console.log(createRandomInt2(1, 3));
console.log(createRandomInt2(1, 3));
console.log(createRandomInt2(1, 3));
console.log(createRandomInt2(1, 3));
console.log(createRandomInt2(1, 3));
console.log(createRandomInt2(1, 3));

 数学的にエレガントなアルゴリズム

下記アルゴリズムは miu_ras さんが質問文中で「エレガントではない」としていますが、数学的に解決する方法の一つだと私は思います。

  1. Math.random() で 1,2,3 の乱数を得る
  2. Math.random() で -1,1 の乱数を得る
  3. 1. と 2. の積を求める

 数学的にエレガントなコード

私が考える数学的にエレガントなコードは「Math.xxxx メソッド、算術演算子だけで完結するコード」です。

function createRandomInt (minInt, maxInt) {
  return (Math.round(Math.random()) * 2 - 1) * (Math.floor(Math.random() * (maxInt - minInt + 1)) + minInt);
}

console.log(createRandomInt(1, 3));
console.log(createRandomInt(1, 3));
console.log(createRandomInt(1, 3));
console.log(createRandomInt(1, 3));
console.log(createRandomInt(1, 3));
console.log(createRandomInt(1, 3));
console.log(createRandomInt(1, 3));
console.log(createRandomInt(1, 3));
console.log(createRandomInt(1, 3));
console.log(createRandomInt(1, 3));

Math.random() に偏りがない前提で考えるならば、

  • 「正の数/負の数」からランダムに偏りなく選びます
  • 「1,2,3」からランダムに偏りなく選びます

 更新履歴

  • 2017/02/03 19:44 算術演算子型コードを追記
  • 2017/02/03 21:30 「数学的にエレガント」の定義に言及
  • 2017/02/03 21:41 配列からランダムに値を得るコードを追記
  • 2017/02/03 22:21 配列からランダムに値を得るコードの別解を追記

Re: miu_ras さん

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 14:01

    「0 が返された場合、1 を返す」この言葉で、もう一つ思いついきました。
    var v = (Math.floor(Math.random() * 6) - 3) || 3;
    論理演算子を使っているので、最初の「数学的な方法」は満たさないのですが、
    これでもまぁまぁシンプルでいい感じですよね。JavaScriptっぽいし。

    > 私が考える数学的にエレガントなコードは
    > 「Math.xxxx メソッド、算術演算子だけで完結するコード」です。

    これは私も同じです。ただ、Math.randomを使うのは
    1回でもどうにかできるはずではないかと思ったことがきっかけです。
    ただそこにこだわりすぎて複雑になりすぎても問題なので
    ご提示のようなコードがいいかもしれませんね。

    ありがとうございました

    キャンセル

  • 2017/02/04 22:24

    > var v = (Math.floor(Math.random() * 6) - 3) || 3;
    これ面白い。一票入れたいw

    キャンセル

  • 2017/02/06 11:22

    今回、正の最小値を任意の数値にした事で考え方が束縛してしまった感がありました。
    皆さんのように1に固定する柔軟な発想が足りませんでした。

    > var v = (Math.floor(Math.random() * 6) - 3) || 3;
    JavaScript 的なコードという意味ではこんなコードも考えたのですが、miu_ras さんのコードの方がすっきりしていいですね。

    function createRandomInt (length) {
    var number = Math.floor(Math.random() * length * 2) - length;
    return number + (number > -1);
    }

    キャンセル

+1

【条件】

  1. 乱数生成は1度のみ行う。
  2. 任意の整数nに対して、-n-n+1, ... -2-112, ... n-1nの計n*2個の整数の内一つを返す。ランダム性は1.で生成した乱数を用いること。ただし、n1以上2**52-1以下とする。(2**52以上の場合は2*nNumber.MAX_SAFE_INTEGERを越えるため)

【考察】
Math.random() [0,1)の乱数
Math.random()*2*n [0,2n)の乱数
Math.floor(Math.random()*2*n) 0, 1, 2, ... 2n-2, 2n-1 の乱数
それぞれnでわると前半は0以上1未満、後半は1以上2未満となる。
fn = i=>Math.floor(i/n) 0, 1, ... n-1, n ... 2n-2, 2n-1 => 0, 0, ... 0, 1, ... 1, 1
そこで次のような関数を考える。
gn = x=>x+1-(2*n+1)*fn(x) 0, 1, ... n-1, n ... 2n-2, 2n-1 => 1, 2, ..., n, -n, -n+1, .. -2, -1
よって
gn(Math.floor(Math.random()*2*n))
(x=>x+1-(2*n+1)*fn(x))(Math.floor(Math.random()*2*n))
(x=>x+1-(2*n+1)*(i=>Math.floor(i/n))(x))(Math.floor(Math.random()*2*n))
関数の中の関数は、引数に副作用が無いため、そのまま評価できるので
(x=>x+1-(2*n+1)*Math.floor(x/n))(Math.floor(Math.random()*2*n)) 
副作用があるMath.random()を引数にとるように式を変形すると
(x=>Math.floor(2*n*x)+1-(2*n+1)*Math.floor(2*x))(Math.random()) 1, 2, 3, ... n-1, n, -n, -n+1, ... -2, -1 の乱数
求めるのはn=3の時なので、
(x=>Math.floor(6*x)+1-7*Math.floor(2*x))(Math.random()) 1, 2, 3, -3, -2, -1 の乱数

【解】

let v = (x => Math.floor(6 * x) + 1 - 7 * Math.floor(2 * x))(Math.random());

数学っぽく考えたつもりですが、Math.random()に副作用があるのでまったく数学っぽくない時点でなんとも言えないです。


【別解】
[0,1) => -n, -n+1, ... -2, -1, 1, 2, ... n-1, n
となる写像関数を直接考える。

  1. 2倍すれば[0,2)となるため、[0,1), [1,2)に分離でき、floorをとれば0, 1になる。
  2. -1を底、1.の値を冪指数とする冪を求めると、1, -1になる。
  3. 2n倍すれば[0,2n)となるため、[0,1), [1,2), ... [n-1, n), [n, n+1), ... [2n-2,2n-1), [2n-1,2n)に分離でき、floorをとれば0, 1, ... n-1, n, ... 2n-2, 2n-1となる。
    このうちの前半は1.の0(2.の1)に、後半は1.の1(2.の-1)に相当する。
  4. さらにnでの余りを求めると0, 1, ... n-1, 0, ... n-2, n-1と前半と後半が同じである。
  5. さらに1足せば1, 2, ... n, 1, ... n-1, nとなる。
  6. 2.と5.を組み合わせれば、目的の写像が得られる。
    f(n)(x) = (-1)^[2x]*([2nx]%n+1)
    ※ fはカリー化されている。^ ... 冪乗。[z] ... ガウス記号、床関数。% ... 剰余。

これをJavaScriptで表すと下記になる。

const f = n => x => (-1)**Math.floor(2 * x) * (Math.floor(2 * n * x) % n + 1)

写像関数が得られたので、n=3の時にxが[0,1)分布の乱数を与えることで答えは

let v = (n => x => (-1)**Math.floor(2 * x) * (Math.floor(2 * n * x) % n + 1))(3)(Math.random());


となる。


【別解2】
※ 途中の演算でMath.MAX_SAFE_INTEGERを越える場合があるため修正。

Math.floorを一回だけにする方法を考える。他にも演算はなるべく1回とする。

2n倍したものにfloorをとると0, 1, ... 2n-2, 2n-1になる。
これを2を除数とした商と剰余を考えると、下記になる。
商: 0, 0, 1, 1, ... 2n-2, 2n-2, 2n-1, 2n-1
剰余: 0, 1, 0, 1, ... 0, 1, 0, 1
つまり、剰余部分は-1を底にしてその値を冪指数にした冪を求めれば
剰余: 0, 1, 0, 1, ... 0, 1, 0, 1 => 1, -1, 1, -1 ... 1, -1, 1, -1
が得られるため、後は商に1足した物と掛ければ、
1, -1, 2, -2, ... 2n-1, -2n+1, 2n, -2n
が得られる。

JavaScriptにすると次のような関数になる。

// q 商、r 剰余
const g = r => q => (-1)**r * (q + 1);


JavaScriptには商を直接求める演算子や関数がないため剰余から求める関数を考える。

// d 被除数、m 除数
const h = d => m => r => (d - r) / m;


除数は2で固定であるため、これを用いると次のようになり、展開しながら変形していく。

const f = x => g(x % 2)(h(x)(2)(x % 2));
// `x % 2`は冗長のためさらに書き替える。
const f = x => (r => g(r)(h(x)(2)(r)))(x % 2);
// 関数を展開する。
const f = x => (r_ => (r => q => (-1)**r * (q + 1))(r)((d => m => r => (d - r) / m)(x)(2)(r_)))(x % 2);
// rはr_と同じ、xとdは同じであり、mは2固定で冗長のためまとめる。
const f = x => (r => (q => (-1)**r * (q + 1))((m => (x - r) / 2)()))(x % 2);
// 固定の所は評価をしてしまって。qもなくす。
const f = x => (r => ((-1)**r * ((x - r) / 2 + 1)))(x % 2);


これに最初の2nを掛けて床関数で整数化したものを与えると[0,1)を均等に配分できる。

const f_ = n => y => (x => (r => ((-1)**r * ((x - r) / 2 + 1)))(x % 2))(Math.floor(2 * n * y));

n=3のときのため、最終的に次のようになる。

let val = (n => y => (x => (r => ((-1)**r * ((x - r) / 2 + 1)))(x % 2))(Math.floor(2 * n * y)))(3)(Math.random());

【別解3】
-1は奇数乗か偶数乗かだけで切り替わることに気付いた。

let val = (x => ((-1)**x * ((x - x % 2) / 2 + 1)))(Math.floor(6 * Math.random()));

IE等の古いブラウザ用に書き直すと

var val = function (x) {return Math.pow(-1, x) * ((x - x % 2) / 2 + 1);}(Math.floor(6 * Math.random()));

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/05 10:40 編集

    1つ目は、少しわかりにくかったのですが、数学的っぽいと感じます。いいですね。

    2つ目(別解)は「**」があってひるんだのですが、ES7proposalなんですね。ES7も勉強しないと。よく見ると、商とべき乗で済んでいて、考え方はシンプルでいいですね。見た目はごちゃっとしていますが…。

    3つ目(別解2)は2つ目とさほど意味合い的には変わらないのだと思いますが「=>」と小カッコが多くてわかりにくかったです…。

    4つ目(別解3)は急にシンプルになった印象でした。
    {0, -1, 0, -1, 0, -1}と{0, 0, 2, 2, 4, 4}を作るあたりは、なるほどと勉強になりました。他にも応用できるかもしれませんし。ありがとうございました

    キャンセル

  • 2017/02/05 11:00

    気付いたのでメモしておきます。
    「(x - x % 2) / 2 + 1」の部分を
    「(x % 3) + 1」にしても行けますね。演算が減ってシンプルになります。

    キャンセル

0

https://teratail.com/questions/64398
上記質問に対するalgさんの回答のパクリですが、、、

List<int> source = new List<int>() {-3,-2,-1,1,2,3};

Random random = new Random();
int randomIndex;
int randomValue;

// 確認
for ( int i = 0; i < 100; i++ )
{
    randomIndex = random.Next(source.Count);
    randomValue = source[randomIndex];
    Console.WriteLine(randomValue);
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 11:05

    いちおうJavaScriptでお願いしたいです。
    回答の方向性としては、私が最初にあげている配列を使う方法がいいということですね。
    ありがとうございました

    キャンセル

0

すみません、全然ダメでした orz

randomを一回だけの呼び出しにして1ラインで、ややこしくない方法でというのは何かありそうな気がしますが以外に難しいですね ><


シンプル(1ライン)かつややこしくないというあたりを狙うならこんなんでもいいんでしょうか

var result = Math.ceil(Math.random()*6-2.5)

追記:ちなみにjavaなどの感覚でceilの結果が浮動小数だと整数として使えないかなと思って
browserのconsoleを使って
var a = [1, 2, 3]
なんてしてからrが1になったときに
alert(a[r])
とやったら2が表示されたので「こんなんでもいいのかな」と思いました。
テキトーな回答で恐縮ですが。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/03 12:09

    random()が(1.5/6, 2.5/6]の範囲内で0です

    キャンセル

  • 2017/02/03 12:13

    負の方向ではfloorにしないと・・・全然ダメでしたorz
    大変失礼しました

    キャンセル

  • 2017/02/04 11:34

    ありがとうございました

    キャンセル

0

こんなのはどうでしょうか?ちょっと長いですが、、

function rdm() {
    var num = parseInt(Math.random() * 7) - 3;
    return num == 0 ? rdm() : num;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 12:10

    再帰ですか…。ありがとうございました

    キャンセル

0

  • MDNのソースそのままの引用で済む。
  • ランダムで出したい数値の範囲が変更されても、配列内の数値を変更するだけでよくメンテナンスしやすい。

配列を使わない理由がないですね。
大量の数列を用意する必要があり、配列に規則性がある場合は、その配列を生成するための関数を作ればいいと思います。今回の場合はそこまでする必要はないですね。

function getRandomInt(min, max) {
  return Math.floor( Math.random() * (max - min + 1) ) + min;
};

const arr = [-3, -2, -1, 1, 2, 3];

let i = getRandomInt(0, arr.length-1);

console.log(arr[i]);

規則性のある配列をまず作り、そこから必要ない数値を除外するというアプローチも簡単にできます。

// [-3, -2, -1, 0, 1, 2, 3] の配列を作り、0を除外
const arr = [...Array(7).keys()].map(v => v - 3).filter(v => v); 

console.log(arr) // [-3, -2, -1, 1, 2, 3]
// 1~10の配列を作り、3、9を除外
const arr = [...Array(10).keys()].map(v => v + 1).filter(v => v !==3 && v !==9); 

console.log(arr) // [1, 2, 4, 5, 6, 7, 8, 10]

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 15:33

    配列方式は柔軟性がある点がいいですよね。
    ありがとうございました

    キャンセル

-3

1~6の乱数を取得して、3引くってのはどうでしょう?相当シンプルだと思いますが。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/03 10:07

    勝手に0がでる仕様なのかな…?
    0~5までを取得して、+1 したあと -3 …

    キャンセル

  • 2017/02/03 10:14

    「0を抜かす」という要件を満たせませんね。

    キャンセル

  • 2017/02/04 09:48

    そういや、そうですね。(酔っ払い)
    得たい数列を配列に仕込んで、その範囲で乱数取得してしまったほうがいいですね。
    数列の順番も初期化時にシャッフルすれば尚いいですね。
    数学的にという質問内容から推測するに、おそらくこんな短い範囲ではないのだろうなあ…と思っていますが。

    キャンセル

  • 2017/02/04 11:29

    それは質問のスタート地点ですよね…。ありがとうございました

    キャンセル

-4

まず前提として「数学的な方法」とはどのようなものであるか定義しないことには具体的な答えはでません。

コンピュータで作る乱数は基本的に疑似乱数に過ぎません(例外はハードウェア乱数生成)。また、乱数の生成過程には様々なものがあります。一様分布、正規分布などモデルがわからないことには確定しません。そして、これが決まれば「候補を配列に持つ」というのは非常にスマートだと思います。

ちなみに、私の出身研究室では、「教授が不確定な外乱要素を乱数で補正するという発明をした」とされており、巻き添え食らわせらかけました。なんか、この発明で特許を取得し、大金を動かしたらしいです。シミュレートするならともかく補正できるのはおかしいだろう、と思います。ただし、万が一にこの外乱要素と疑似乱数の生成過程が同一であるなら可能性もあるかもしれません。もっとも、私は別の可能性を考えましたがね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/04 12:13

    一応、私の考えていた「数学的な方法」の定義を書きました。その条件に基づき回答をお願いします。
    必ずしも条件にこだわらないと変更しましたが、「数学的な方法」に沿った回答も引き続きお待ちしています。


    キャンセル

  • 2017/02/04 22:37

    「プログラム言語で記述しやすい」ということが「いい方法」の要件であると仮定します。であれば、既出の「候補を配列にもつ」というデータ構造と「添え字を乱数で取得する」というアルゴリズムがもっとも素直だと思います。なお、乱数分布、生成過程は既存のMathクラスに丸投げするとします(自分が当初考えていたことは深読みだったようですね)

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.52%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • JavaScript

    16382questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。