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

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

新規登録して質問してみよう
ただいま回答率
85.48%
アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Q&A

解決済

9回答

5884閲覧

平均的に振り分けるアルゴリズム

kirato

総合スコア32

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

0グッド

0クリップ

投稿2019/03/22 02:07

編集2019/03/22 04:51

前提・実現したいこと

平均的になるようにデータを振り分けたい
例)
テーブルA,B,C,Dにそれぞれ20件,30件,50件,100件データが入っているとします。
テーブルXに100件データがありA,B,C,Dに振り分ける場合
テーブルA,B,C,Dが出来るだけ均等になるようにしたいのですが
どのようなアルゴリズムが考えられるでしょうか。

私が思いつく限りでは、
A,B,C,Dの件数を取得して一番小さいものに1件づつ登録していく方法なのですが
他に方法はないものかと思っています。
出来ればAに何件、Bに何件と計算する方法があれば教えて頂きたいです。

以上よろしくお願いします。

■■仕様
いろいろご指摘を頂いたので具体例を追記します。

まず、A,B,C,Dからデータを取り出すことは出来ません。XをA,B,C,Dに振り分けるのみです。
毎日Xは増えますが、増える件数は変わります。
1日目:X=100件とすると
A,B,C,D=67,67,66,100
2日目:X=50だとすると
A,B,C,D=84,83,83,100
3日目:X=100だとすると
A,B,C,D=113,113,112,112
という感じです。

mts10806さんからのご指摘のあった
「A~Dが全て八百屋でX配送センターからキャベツが100個くるから」
というのがすごく分かりやすいです。

これが毎日Xセンターから届くのでA~Dの店舗のキャベツの在庫が均等になるようにしたいといった感じです。A~Dの在庫数は事前に分かります。
キャベツは売れるし、他のセンターから個別に入荷する場合もありますので
Xセンターで在庫数を出来るだけ均等にしたいといった内容です。

以上よろしくお願いします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2019/03/22 02:17

「テーブルXに100件データがあり」というのは振り分けを開始する段階でわかっている条件でしょうか? あと、100件を割り振った後は「A、B、Cが66~67件、Dには1つも割り振られず100件」という形で間違いないでしょうか。
stdio

2019/03/22 02:19

よく分からないので、例を順に追っていきます。 1)単に平均出すだけなら、num = (A+B+C+D)/4で可能です。 2)Xをそれぞれの個数、X/4ですれば可能です。 > A,B,C,Dの件数を取得して一番小さいものに1件づつ登録していく方法 テーブルの大きさ取得して、一番大きいものが一番小さいのもに1件データを受け渡す。 これを再起的に行えば可能ですが、処理が重いのでおススメしません。 こんなところですかね...
kirato

2019/03/22 02:33

早速の回答ありがとうございます。 maisumakun様 >「テーブルXに100件データがあり」というのは振り分けを開始する段階でわかっている条件でしょうか?  件数は振り分ける段階で分かっています。 >あと、100件を割り振った後は「A、B、Cが66~67件、Dには1つも割り振られず100件」という形で間違いないでしょうか。  間違いありません。 stdio様 質問の仕方が悪かったようで申し訳ありません。 >テーブルの大きさ取得して、一番大きいものが一番小さいのもに1件データを受け渡す。 >これを再起的に行えば可能ですが、処理が重いのでおススメしません。  一番大きいものからではなく、Xのデータを振り分けるだけですので  例でいえばループは100回になります。  どっちにしても処理が重いのでどうにか出来ないかなーといった感じです。 平均値で計算するのであればA+B+C+D/4で50 Aに30件、Bに20件でA,B,C,Dが平均以上になるので さらに平均をだして・・・をXが無くなるまで繰り返すといった感じでしょうか
m.ts10806

2019/03/22 03:01 編集

個人的には「データベースのデータ」って何らかの意味と意図をもって保管されているので、 「振り分ける」といって機械的に振り分けることが本当にいいことかどうかが分かりませんね。 A~Dが全て八百屋でX配送センターからキャベツが100個くるから、というのとは違うと思いますし。 各テーブルの関係性も含めて例を具体的にされた方がいいです。
guest

回答9

0

clalalala さんの答えににていますが。

(20+30+50+100) + 100 = 300件
300件 / 4つのテーブル = 75件/テーブル
しかしDは既に75件を超えているので、除外してもう一度計算する。

(20+30+50) + 100 = 200件
200件 / 3つのテーブル = 66.66..件/テーブル

なのでそれぞれ
A:66-20=46件追加
B:66-30=36件追加
C:66-50=16件追加

46+36+16=98件なので端数(2件)分は適当にABに割り振る、とかで良いのでは。

投稿2019/03/22 02:50

torisan

総合スコア678

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

こんにちは。

こんなのはどうでしょう?(ばらつきを最小化する、ただし既に振り分けてあるものは取り出さないとして考えてみました。)

  1. 最初に件数の少ない順にソートし、n=1とする。

今回の場合、A(20), B(30), C(50), D(100)の順序になります。

  1. 先頭からn個選択し、そのn個に振り分けた時n+1番目の件数を越えるか否か判定する

n=2の場合、AとBへ振り分ける件数は{Aの件数(20)+Bの件数(30)+振り分けたい件数(100)/n=75となり、これはCの件数50以上なので「越える」です。(nで割った結果は切り捨て)

  1. 越えなくなるまでnを1づつ増やして2を繰り返す。

ただし、nの最大値(項目数)に到達していたら、そこで繰り返し終了

  1. 2.で求めた件数になるように振り分ける。

この時端数分が余るので、余りをn個に順番に振り分ける。(順序は任意)

投稿2019/03/22 03:55

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kirato

2019/03/22 05:02

回答ありがとうございます。 この方法で実装してみます。
guest

0

x.py

python3

1def pay(stocks, num): 2 n = len(stocks) 3 after = [stocks[i] for i in range(n)] 4 for i in range(num): 5 target = after.index(min(after)) 6 after[target] += 1 7 return [after[i] - stocks[i] for i in range(n)] 8 9stocks = [20, 30, 50, 100] 10print("stock:", stocks) 11 12for num in [100, 50, 100]: 13 pays = pay(stocks, num) 14 print(" pay:", pays) 15 stocks = [stocks[i] + pays[i] for i in range(len(pays))] 16 print("stock:", stocks)

実行例
イメージ説明

キャベツを一つずつ在庫が一番すくないところに配るようにしています。
質問文にある実行例とおなじ結果が得られています。
もっと計算量をすくなくすることはできそうですが。

参考情報

  • 整数をできるだけ均等になるように n分割

https://qiita.com/keisuke-nakata/items/c18cda4ded06d3159109

  • 離散数学「ものを分ける理論」 問題解決のアルゴリズムをつくる (ブルーバックス)

https://www.amazon.co.jp/dp/B07D34XD57

投稿2019/03/23 03:50

編集2019/03/23 03:51
katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kirato

2019/03/23 05:27

既に解決済みとしているのにわざわざありがとうございます。 1つづつ配るしかないかなと思っていましたがどうやって実装するか迷っていましたので 大変参考になりました。
katoy

2019/03/23 05:41

> ... 既に解決済みとしているのに ... どの回答とも違う方法を具体的なコードで提示しておきたかったのです。 質問者さんがえらんだ エストアンサーでの方法で、質問者さんがどんなコードを書いたかを差し支えなければ知りたいです。 一つずつ配るのでhなく、一発で配達個数を計算する方法をどなたかが示してくれると嬉しいです。
guest

0

総件数は300,最大の件数100に揃えるには1004=400必要で、件数が足りません。
最大のDを除外すると、総件数300-100=200,最大の件数50となり、Cまでの3テーブル揃えるためには50
3=150なので、足ります。
まず50に揃えるのに50使い、残りの50を3テーブルで均等に割り振ればいいことになります。

投稿2019/03/22 02:57

swordone

総合スコア20651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kirato

2019/03/22 04:37

回答ありがとうございます。 A~Dの平均を算出して、平均以上のものを除外して平均になりように足す。 A~Dが平均以上になるともう一度平均を算出して平均になるように足す。 これをXが無くなるまで繰り返すといった感じでしょうか。
guest

0

ざざっと考えれば、以下のようになります。

  1. (すでに入っている項目数 + これから入れる項目数) / 分ける列数、を計算する
  2. 1の結果が現在の最大値を超えていれば、それで分割を進める
  3. 現在の最大値を超えていない場合、最大値のものを除外して1からやり直す

投稿2019/03/22 02:41

maisumakun

総合スコア145184

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

答えとしては完全ではないですが、私の考えるところを回答にしてみます。

一番小さいものに1件づつ登録

ということはそれぞれデータが何件あるかは把握できている上で振り分けていくことになるわけですよね。
結構、「平均的」というのは難しい言葉にはなります。

単純に計算すると、
0. A,B,C,D,Xの件数総合計からXを除いたテーブル数を割る(平均値が出る)
0. 平均値から各テーブルの件数を引く(過不足が分かる)
0. 不足しているところにその件数分、Xの100件から振り分ける

・・・という流れになります。


「不足」は良いとして「過」、つまり平均より多いテーブルに対してどういう対応を行うかです。
実際は、いずれのテーブルもそんなに綺麗な数値になるわけではないでしょうし、
不足分に振り分けたものの今回の100件から残った場合、どうするか?がネックになってきます。

だから「平均的」というのは難しい状態になります。

「過不足」に対してどうするかは、アルゴリズムの前、「仕様」部分になります。
つまり**「平均的」「出来るだけ均等」という言葉自体の定義を「具体的」にする必要があります**。
「自動的に振り分ける」としても例えAIとしても何かしら基準となる仕様は必要になります。
その仕様に則って振り分けを行っていくことになりますので。

今回は(20+30+50+100+100)/4で「平均値」は75となりますね。
ということはAに55,Bに45,Cに25 必要となりますが、必要な件数は125件になるので、Xの100件では足りません。
DからCに25補填するのか、A~Dの件数は変えない(つまりDは100のまま)、A~Cを同じ数に出来るように割っていくのか(100/3なので割り切れませんが)

このあたりは決めてください。

投稿2019/03/22 02:25

m.ts10806

総合スコア80850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

A,B,C,D,Xの件数を全部足して、4で割った値が理想の件数なので、それを整数単位にまるめてから、それと現在の差分だけ足せばいいのでは?

#追記
例の数字だと、Dの件数が、理想件数を超えているので、除外して、残りA,B,Cに対して同じロジックを再帰呼び出しですかね。

投稿2019/03/22 02:18

編集2019/03/22 05:13
otn

総合スコア84540

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2019/03/22 02:22

もともとDにあったものを取り出して構わないのか、現時点ではそれもわからないですね…
stdio

2019/03/22 02:39

A,B,C,D,Xの件数を全部足して、5では? 平均をもとめるのになぜ4!!
m.ts10806

2019/03/22 02:47

stdioさん Xのデータを振り分けてA,B,C,Dを平均的にしたいのでXにはデータ残らない想定になります。なので4で割る。
stdio

2019/03/22 02:50

ああ、なるほど。よく読んでませんでしたわ。失礼しました。 しかし、質問者の質問の内容もいまいち理解できませんでしたわ。 平均的な振り分けなら普通はすぐできると思いますがね...
m.ts10806

2019/03/22 02:55

そこが問題ですね。平均「的」 本来は「仕様」としてかっちり決めなきゃいけないところ、 質問者の「気分」にもなるところが明文化されてません。
stdio

2019/03/22 03:03

質問者の「気分」というか、見せ方にばらつきがあるのが嫌なんでしょう。 私ならデータはこれだけですと正直に、それぞれのシートにテーブルおいておきますが...
kirato

2019/03/22 04:17

質問の仕方に問題があったようで申し訳ないです。 A,B,C,Dからデータを取り出すことは出来ません。XをA,B,C,Dに振り分けるのみです。 毎日Xは増えますが、増える件数は変わります。 1日目:X=100件とすると A,B,C,D=67,67,66,100 2日目:X=50だとすると A,B,C,D=84,83,83,100 3日目:X=100だとすると A,B,C,D=113,113,112,112 という感じです。 mts10806さんが言われたいた 「A~Dが全て八百屋でX配送センターからキャベツが100個くるから」というのは すごく分かりやすいです。これが毎日Xセンターから届くのでA~Dの店舗のキャベツの在庫が均等になるようにしたいといった感じです。A~Dの在庫数は事前に分かります。 キャベツは売れるし、他のセンターから個別に入荷する場合もありますので Xセンターで在庫数を出来るだけ均等にしたいです。
m.ts10806

2019/03/22 04:19

要件は質問の肝となる部分なので個別コメントにするのは良くありません。 質問本文を編集してそれまでの指摘内容を含めて編集してください。
stdio

2019/03/22 04:59

冷たい事言いますが、こんな投稿を共有しても誰も得しないし、意味がないと思います。 ベストアンサー選んでさっさと質問を閉じて下さい。
kirato

2019/03/22 05:00

すみません、質問の方に追記します。
m.ts10806

2019/03/22 05:02

stdioさんの意見に賛成です。 回答を参考に最終的には自分で考えてください。 アルゴリズムは考えないと育ちません。 もし学校の課題とかをやってもらおうという魂胆があったりしたのでしたら、今後回答はつかないと思います。 丸々そのままの回答を欲しがるって質問じゃなく作業依頼ですから。
kirato

2019/03/22 05:15 編集

確かに需要はないですね、私もこんなお願いされたのは初めてでしたので。 皆さまの意見を参考にあとは自分で考えます。 ありがとうございました。
stdio

2019/03/22 05:23

kiratoさん。 親切な人が8人も集まって良かったですね。 お願いされたのは初めてとか関係なく、平均も出せないなんて小学生以下ですからね。 私だけなら、見捨てていましたよ。 VBAは戻り値の書き方とかが独特なので注意しながらこれからも頑張ってください。
m.ts10806

2019/03/22 05:25

stdioさん VBAの話はどこから・・・
stdio

2019/03/22 05:30

すまん、データとかテーブルとか言ってたからつい... 通常業務で行うのはExcelで管理できるVBAが最適化と... やっぱ作業しながらだと判断力の低下と気分に流されるのでダメですね。
guest

0

どういう結果になって欲しいか、サンプルがあった方が良いと思いますが、テーブルの件数からという事なので、SQLで求めてみました。
ただ、割った数の余りの扱いまでは考慮していません。
※各テーブルの件数は切り捨てして、切り捨て後の合計値を処理の件数から差し引いた件数を、
何処かに寄せる事になるかと思います。

SQL

1--ちょっと考え違いしてたので、削除

※with式が使えるDBMSならもうちょっと記述はシンプルになるかと思います。

投稿2019/03/22 03:31

編集2019/03/22 04:10
sazi

総合スコア25188

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kirato

2019/03/22 04:22

具体的なSQLまで教えて頂きありがとうございます。
sazi

2019/03/22 06:19

見直して再帰で考えてみましたが、複雑になりすぎて断念しました。 DBサイドで行うとしても、ストアドでのループ処理になりますね。
guest

0

(20+30+50+100) + 100 = 300件
これを、A〜Dの4つに分けるので、1つにつき、平均 300 / 4 = 75件
もうすでに100件のデータがあるDを除外して、A〜Cに均等に振り分けるためには、それぞれ、あとA:55件,B:45件,C:25件のデータが追加されればいいので、新しく追加する100件のデータをランダムに並べ替えて、55:45:25 = 11:9:5になるようにスライスすればよいと思います。

トータルで100件なので、44件:36件:20件で、ちょうど100件になります。

もうすでにあるデータも含めランダムに配分するのであれば、300件をランダムに並べ替えて、この比率にスライスすれば、均等になります。

投稿2019/03/22 02:23

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

swordone

2019/03/22 02:29

それ追加後が64,66,60であまり均等になってない
退会済みユーザー

退会済みユーザー

2019/03/22 03:31

3つの均等さではなく4つの均等さなので、Dも含め考えなければなりません。 追加データが100件から200件になるにつれて、平均100件に近似します。 そして、これは、Dを除外した場合のみに限ります。 追加データが200件を超えたら、Dも含めて考えなければなりません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問