[[20190102084214]] 『ランダムに抽出』(前) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『ランダムに抽出』(前)

 お世話になります。

 シフト表(2019年1月のもの。実際の人数は20人です。)

 回数       1   2   1   1   1   1       1   2           回数
 日付   1   2   3   4   5   6   7   8   9   10...31  
 曜日   火  水  木   金  土  日   月  火  水   木   木  
  あ    a   b  休   a   b   休  休   a   b   日    a       1
  い    休  休  a    b  休   a   b   休  a    b   休       1
  う    b   休  a   休  日   日  a   休   b   休   b       3

 抽出結果

 氏名  日付
  あ   1/10
  い   1/5
  う   1/2   1/4   1/8

 (1) 一行目の回数の数字に応じてその日の"休"または"日"の人をランダムに抽出したい。
 (2) 一人辺りの一ヶ月の抽出は右端の回数だけ行う。
 (3) "休"が連続するときはその人は抽出しない。("日"は連続しても抽出対象)
 (4) 抽出結果は複数パターン表示したい。

 しばらくずっと考えているのですが、とっかかりすら得られず。
 よろしくお願いします。

< 使用 Excel:Excel2010、使用 OS:Windows7 >


 回答ではありません。 m(__)m
 単に疑問点の確認だけ・・・

 どんな目的に使うんですか? イメージが涌かないんですけど・・

 > "休"が連続するときはその人は抽出しない。

 シフト表で「休」が連続した場合ですね?
  その場合、連続開始日(=先頭)の「休」も対象外ってことですか?

 >抽出結果は複数パターン表示したい。
 こう言う仕様って、キリがない気がするんですが、どう言う趣旨なんですか?
  全パターン表示?
  100パターンあったら一割くらい(10パターン)表示?
  2パターン以上あっても、2パターン表示でいい? 

(半平太) 2019/01/02(水) 15:56


 ありがとうございます。

 >どんな目的に使うんですか?

 仕事に関することで説明が難しいです。休みの人を指定する必要があるとだけ。今まで手作業でやっていたのを自動でランダムに振り分けて、そこから都合の悪い人だけを変更するといった形で使用したいと思っています。

 >その場合、連続開始日(=先頭)の「休」も対象外ってことですか?

 そうです。

 >こう言う仕様って、キリがない気がするんですが、どう言う趣旨なんですか?

 乱数で調べるて見ると、RANDBETWEEN関数というものがありました。これのように、再計算ごとに変化するという使い方が出来ればと考えていました。
 もしかしたら根本的に勘違いしているかもしれません。
 前述のとおり振り分けた結果から人の手で仕分けしようと思ってますので、パターンはなくても問題ないかもしれません。

 分かりにくくてすみません。
(前) 2019/01/02(水) 16:22

 機械的に出来るかも、と思っていたのですが、結構厄介ですねぇ・・

 月の初めの方で、適当に(機械的に)割り振って行くと、
 後の方の日で、その日はノルマ達成した人しか残ってないなんて事態が発生する可能性があります。

 そうなると、その日はまだノルマ達成していないようにすべく、
 月初の方で割り当てたのを取り消し、他の人に割り当てる、なんて後追い操作が必要になります。

 そもそも、日別回数合計 と 人別回数合計 ってどんな関係にあるんでしょうか?

 日別回数合計 > 人別回数合計

 だったら、絶対に無理ですよね?

( 半平太) 2019/01/02(水) 23:01


 おはようございます。

 >そもそも、日別回数合計 と 人別回数合計 ってどんな関係にあるんでしょうか?
 >日別回数合計 > 人別回数合計
 >だったら、絶対に無理ですよね

 日別合計回数(使わせていただきます)は毎月29回と決まってます。なので必ず複数回割り当てられる人が出ます。

 よろしくお願いします。(といってもエクセルの通常機能で難しいのであれば、また別の手段を考てみます)
(前) 2019/01/03(木) 06:21

 日別合計回数が毎月29回は分かりましたが、全貌が依然としてハッキリしません。

 1.29回の内訳をどの日に割り振るのか、また、誰に何回やらせるかは、どう決まるんですか?

 ※ それらの数字が先に決まるんですよね?
               ↑
    これ大事なんですけど。なんか「結果集計」なのかな、との疑念も生じているんですが・・
    つまり、個人別は、2回を超えないように適当に割り振ればいいだけなのかも知れないなぁ・・と。

 2.こういう関係にはならないことがハッキリしているのかも知りたいんですが?(結果集計なら等しくなるので、そんな事態になる筈もないですが)
    ↓
  「日別合計回数(多い) > 人別合計回数(少ない)」

( 半平太) 2019/01/03(木) 07:51


 >1.29回の内訳をどの日に割り振るのか、また、誰に何回やらせるかは、どう決まるんですか?

 29回の内訳は日付は固定です。(変わる可能性もあるので最初の表は適当です。実際は1日と30日、31日を除く日で28日だけ2人指定します)

 シフト表ができてから、この人に何回振り分ける、というように決めてます。ある程度は決まってますが、その月いない人もいるので、その場合は別の人がもらいます。

 >「日別合計回数(多い) > 人別合計回数(少ない)」

 必ず等しいです。割り振られている日は必ず人を指定しなければならないので、
 「日別合計回数=人別合計回数」となります。
(前) 2019/01/03(木) 08:48

1月の実際のデータを提示してもらえませんか?
氏名等はむろんアルファベットで結構です。
知りたいのは、休、日などがどの程度の密度であるかです。
また、検証にも皆さん共通に使えるのではないですか?

(γ) 2019/01/03(木) 10:11


少しお待ちください。
一ヶ月丸ごとだと見にくいかもしれません。
(前) 2019/01/03(木) 10:46

 お待たせしました。割り振りがないのは30日じゃなく21日でした。

     1  1 1  1  1 1  1 1 1  1  1  1  1  1  1 1 1  1  1    1  1 1  1  1 1  2  1 1
  1  2  3 4  5  6 7  8 9 10 11 12 13 1415 16 1718 19 2021 22 2324 25 2627 28 293031
  火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水木
 あ休      休       休     休       休       休      休      休       休      休     休
 い           休 休      休      休       休      休      休            休      休
 お     休                       休 休 休        休       休      休       休      休
 か  休       休      休      休       休                               休      休
 き  休       休      休      休       休       休      休      休
 く休      休       休     休       休       休      休      休       休      休     休
日 1  2  3 4  5  6 7  8 9 10 11 12 13 1415 16 1718 19 2021 22 2324 25 2627 28 293031
 け              休              休         休      休      休       休      休     休
 こ  休       休      休      休       休       休      休      休       休      休
 さ      休 日休 休 日
 し休      休      休      休        休      日                           休       休
 す  休       休      休      休       休       休      休      休       休
 せ  休       休                       休              休      休       休      休
 そ      休      休     休       休      休 日                            休      休
日 1  2  3 4  5  6 7  8 9 10 11 12 13 1415 16 1718 19 2021 22 2324 25 2627 28 293031
 た      休      休      休      休       休      休      休       休      休      休
 ち      休      休      休      休       休      休      休       休
 つ休      休      休                               休       休      休   休      休
 て  休       休       休     休        休      休      休       休      休
 と  休       休       休     休        休休 日  日      休       休      休      休
 な                                   休       休      休日 日  日

   人回数  日付
 く  2      16,28
 け  2       19,28
 さ  1       4
 し  2      10,13
 す  2      8,17
 せ  2      20,29
 そ  3      3,9,30
 た  2      15,27
 ち  4      6,12,18,24
 つ  3      7,22,24
 て  3      2,11,26
 と  2      5,23
 な  1      14
(前) 2019/01/03(木) 11:29

申し訳ないけど判定不能です。
Excelのセル範囲をコピーして、そのままここに貼り付けたらどうなりますか?

(γ) 2019/01/03(木) 11:50


会社のPCデータのため持ち出しできないです。
今はスマホから作成したので余計見にくいですね。少し修正してみます。
(前) 2019/01/03(木) 11:54

 休と日の日付データだけでも良いですか?ひらがなめちゃくちゃだったのでアルファベットにします。

 a 休 1、4、7、10、13、16、19、22、25、28、31
 b 休 5、6、9、12、15、18、21、26、29
 c 休 3、12、13、14、18、21、24、27、30
 d 休 2、5、8、11、14、26、29
 e 休 2、5、8、11、14、17、20、23
 f 休 1、4、7、10、13、16、19、22、25、28、31
 g 休 6、12、16、19、22、25、28、31
 h 休 2、5、8、11、14、17、20、23、26、29
 i 休 3、5、6
   日 4
 j 休 1、4、7、11、27、30
   日 16
 k 休 2、5、8、11、14、17、20、23、26
 l 休 2、5、14、20、23、26、29
 m 休 3、6、9、12、15、27、30
   日 16
 n 休 3、6、9、12、15、18、21、24、27、30
 o 休 3、6、9、12、15、18、21、24
 p 休 1、4、7、19、22、25、28、31
 q 休 2、5、8、11、14、17、20、23、26
 r 休 2、5、8、11、14、17、20、23、26、29
 s 休 14、17、20
   日 21、22、23
(前) 2019/01/03(木) 13:06

再現できてるでしょうか?
日別回数合計と人別回数合計は、理解できませんでした。

   _____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____
  |_____|__A_|__B_|__C_|__D_|__E_|__F_|__G_|__H_|__I_|__J_|__K_|__L_|__M_|__N_|__O_|__P_|__Q_|__R_|__S_|__T_|__U_|__V_|__W_|__X_|__Y_|__Z_|_AA_|_AB_|_AC_|_AD_|_AE_|_AF_|
  |___1_|
  |___2_|       1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31
  |___3_|      火   水   木   金   土   日   月   火   水   木   金   土   日   月   火   水   木   金   土   日   月   火   水   木   金   土   日   月   火   水   木
  |___4_|  a   休             休             休             休             休             休             休             休             休             休             休
  |___5_|  b                       休   休             休             休             休             休             休                       休             休
  |___6_|  c             休                                           休   休   休                  休             休             休             休             休
  |___7_|  d        休             休             休             休             休                                                          休             休
  |___8_|  e        休             休             休             休             休             休             休             休
  |___9_|  f   休             休             休             休             休             休             休             休             休             休             休
  |__10_|  g                            休                            休                  休             休             休             休             休             休
  |__11_|  h        休             休             休             休             休             休             休             休             休             休
  |__12_|  i             休   日   休   休
  |__13_|  j   休             休             休                  休                       日                                                     休             休
  |__14_|  k        休             休             休             休             休             休             休             休             休
  |__15_|  l        休             休                                           休                            休             休             休             休
  |__16_|  m             休             休             休             休             休   日                                                     休             休
  |__17_|  n             休             休             休             休             休             休             休             休             休             休
  |__18_|  o             休             休             休             休             休             休             休             休
  |__19_|  p   休             休             休                                                          休             休             休             休             休
  |__20_|  q        休             休             休             休             休             休             休             休             休
  |__21_|  r        休             休             休             休             休             休             休             休             休             休
  |__22_|  s                                                                    休             休             休   日   日   日

(チオチモリン) 2019/01/03(木) 14:16


Good jobです。これで皆さんも取っつきやすくなったものと思います。
ありがとうございます。取り急ぎ。
(γ) 2019/01/03(木) 14:20

分りにくいですね。
別パターンで再掲します。

 ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
 │ │ 1│ 2│ 3│ 4│ 5│ 6│ 7│ 8│ 9│10│11│12│13│14│15│16│17│18│19│20│21│22│23│24│25│26│27│28│29│30│31│
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │ │火│水│木│金│土│日│月│火│水│木│金│土│日│月│火│水│木│金│土│日│月│火│水│木│金│土│日│月│火│水│木│
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │a │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │b │ │ │ │ │休│休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │休│ │ │休│ │ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │c │ │ │休│ │ │ │ │ │ │ │ │休│休│休│ │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │d │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │ │ │ │ │ │ │ │休│ │ │休│ │ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │e │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │ │ │ │ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │f │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │g │ │ │ │ │ │休│ │ │ │ │ │休│ │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │h │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │i │ │ │休│日│休│休│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │j │休│ │ │休│ │ │休│ │ │ │休│ │ │ │ │日│ │ │ │ │ │ │ │ │ │ │休│ │ │休│ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │k │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │l │ │休│ │ │休│ │ │ │ │ │ │ │ │休│ │ │ │ │ │休│ │ │休│ │ │休│ │ │休│ │ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │m │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│日│ │ │ │ │ │ │ │ │ │ │休│ │ │休│ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │n │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │o │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │ │ │ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │p │休│ │ │休│ │ │休│ │ │ │ │ │ │ │ │ │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │q │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │r │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤
 │s │ │ │ │ │ │ │ │ │ │ │ │ │ │休│ │ │休│ │ │休│日│日│日│ │ │ │ │ │ │ │ │
 └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘

(チオチモリン) 2019/01/03(木) 14:26


 チオチモリンさんありがとうございます。

 >日別回数合計と人別回数合計は、理解できませんでした。 

      1    1    1    1    1    1    1    1    1    1   1    1    1    1    1    1    1    1    1         1    1    1    1    1    1    2    1    1   =29 
 1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31

   人回数  日付
 f  2      16,28
 g  2       19,28
 i  1       4
 j  2      10,13
 k  2      8,17
 l  2      20,29
 m  3      3,9,30
 n  2      15,27
 o  4      6,12,18,24
 p  3      7,22,24
 q  3      2,11,26
 r  2      5,23
 s  1      14
   =29

 こういうことなのですが、最初の説明を間違えてたらすみません……。
(前) 2019/01/03(木) 14:48

 (1) 一行目の回数の数字に応じてその日の"休"または"日"の人をランダムに抽出したい。
 (2) 一人辺りの一ヶ月の抽出は右端の回数だけ行う。
 (3) "休"が連続するときはその人は抽出しない。("日"は連続しても抽出対象)

 ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬──┬──┬──────┐
 │ │ │ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ 1│ │ 1│ 1│ 1│ 1│ 2│ 1│ 1│ 1│ 1│ │  │ 29 │      │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼──┼──┼──────┤
 │ │ 1│ 2│ 3│ 4│ 5│ 6│ 7│ 8│ 9│10│11│12│13│14│15│16│17│18│19│20│21│22│23│24│25│26│27│28│29│30│31│  │  │      │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼──┼──┼──────┤
 │ │火│水│木│金│土│日│月│火│水│木│金│土│日│月│火│水│木│金│土│日│月│火│水│木│金│土│日│月│火│水│木│  │  │      │
 ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼──┼──┼──────┤
 │a │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│  │  │      │
 │b │ │ │ │ │休│休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │休│ │ │休│ │ │  │  │      │
 │c │ │ │休│ │ │ │ │ │ │ │ │休│休│休│ │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │  │  │      │
 │d │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │ │ │ │ │ │ │ │休│ │ │休│ │ │  │  │      │
 │e │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │ │ │ │ │  │  │      │
 │f │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│  │ 2 │16,28    │
 │g │ │ │ │ │ │休│ │ │ │ │ │休│ │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│  │ 2 │19,28    │
 │h │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │  │  │      │
 │i │ │ │休│日│休│休│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │  │ 1 │4      │
 │j │休│ │ │休│ │ │休│ │ │ │休│ │ │ │ │日│ │ │ │ │ │ │ │ │ │ │休│ │ │休│ │  │ 2 │10,13    │
 │k │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │ │  │ 2 │8,17    │
 │l │ │休│ │ │休│ │ │ │ │ │ │ │ │休│ │ │ │ │ │休│ │ │休│ │ │休│ │ │休│ │ │  │ 2 │20,29    │
 │m │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│日│ │ │ │ │ │ │ │ │ │ │休│ │ │休│ │  │ 3 │3,9,30   │
 │n │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │  │ 2 │15,27    │
 │o │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │ │ │ │  │ 4 │6,12,18,24 │
 │p │休│ │ │休│ │ │休│ │ │ │ │ │ │ │ │ │ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│  │ 3 │7,22,24   │
 │q │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │ │ │ │  │ 3 │2,11,26   │
 │r │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │休│ │ │  │ 2 │5,23    │
 │s │ │ │ │ │ │ │ │ │ │ │ │ │ │休│ │ │休│ │ │休│日│日│日│ │ │ │ │ │ │ │ │  │ 1 │14     │
 └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴──┴──┴──────┘

残念ながら(1).(3)は理解できないです。
そもそもこの表が、「抽出」前のものなのか、「抽出」後の完成品なのかもわからないです。

(チオチモリン) 2019/01/03(木) 15:12


 表はただのシフト表です。
 抽出という言葉が不適切でした。
 シフト表があって、1人辺りの割り振り回数を決めたら、その回数分”休”と”日”にあたる日付をランダムに選択したい、ということです。

 上の人回数の日付は1月の実際のデータです。
(前) 2019/01/03(木) 15:25

誤って保存せずに時間のかかる処理を始めてしまった。
散歩して来ます。
戻っても終了していない悪寒w
(γ) 2019/01/03(木) 15:58

 こんばんは!
思いっきり力業ですけど、何かの足しになればと、、、、

 ちょっと全体を表示できないんで簡易版ですけど、
そのシフト表の隣にAH列に=rand()を入力しておきます。

 次に、上の三段日付や曜日のところは=B1としてリンクさせておきます。
次に、AI4に=INDEX($A$4:$AF$22,RANK($AH4,$AH$4:$AH$22,ROW(A1)),COLUMN(A1))
と入力して配列(Ctrl+Shift+Enter)で確定して右に下に必要数フィルします。

 0 表示が気になりますから表示形式を # にして 0 を消します。

 で、F9を押すとシフト表が並び替わりますから、一番上に来た人から順に人数に応じて色をつけます。
これを、1日から31日まで繰り返します。おみくじ感覚で楽しみながら(笑)
抽出する人は上から順番です。←これはルールの一例です。

 休が並んでいる時は、どうするとか細かいルールはトピ主さんが決められればいいでしょう?

 ルールと法則とか色々あるでしょうけど、人力で考えるよりはましかなと(笑)
どうでしょう?
                =B1
			1	1	1	1	1	1
		1	2	3	4	5	6	7
		火	水	木	金	土	日	月
   =rand()      =INDEX($A$4:$AF$22,RANK($AH4,$AH$4:$AH$22,ROW(A1)),COLUMN(A1))
0.284404412	c			休				
0.401876686	f	休			休			休
0.691038187	j	休			休			休
0.272445387	b					休	休	
0.352693552	d		休			休		
0.504246398	g						休	
0.573673344	i			休	日	休	休	
0.751771116	l		休			休		
0.790380459	o			休			休	
0.924922698	r		休			休		
0.740389563	k		休			休		
0.773408391	m			休			休	
0.903608033	p	休			休			休
0.564580652	h		休			休		
0.777411999	n			休			休	
0.360763772	e		休			休		
0.904633142	q		休			休		
(SoulMan) 2019/01/03(木) 16:46

 SoulManさんありがとうございます。
 力技···いやいやシンプルな方法ですよ!
 最終的に判別するのは人の目ですからね···。並び順だけランダムに、残りは人がというのは臨機応変に対応出来ると思います。
 感謝です。
(前) 2019/01/03(木) 19:06

 いやあ結構むずかしい。
 N クイーン問題みたいにトライしてみたけど、何せあり得る組み合わせは膨大なので
 件の最初のトライは、4時間弱たっていたが終了せず。泣く泣く Kill しました。

 ランダムに検索する方法に変えてみたけど、なかなかヒットしません。
 ただし、幸運にも下記の別解が見つかりました。

 f 2    10,13
 g 2    22,28
 i 1     4
 j 2    16,27
 k 2     5,20
 l 2    26,29
 m 3     3,12,30
 n 2    15,24
 o 4     6,9,18,24
 p 3     7,19,28
 q 3     2,8,11
 r 2    14,23
 s 1    17

 これだと余り褒められないな。
 もっとエレガントなものをどなたかお願いします。

(γ) 2019/01/03(木) 22:58


 抽出の優先順位を にしてみたら、簡単にヒットしました。
           ↓
   今後の選択肢数(昇順)>残回数(降順)>最終割当日(昇順)>名簿(ランダム)

 1月用データが、たまたまその処理に合っていたのかも知れません。
 データの有り様で難易度が上がったり下がったりするんでしょうね・・多分

 作業用エリアとして AI〜BV列を使用します。

 優先順位を付けているので、バリエーションは殆ど無いです。

 結果は、AI4セル以下に表示されます。

 <サンプル>
  行 ___A___ _B_ _C_ _D_ _E_ _F_ _G_ _H_ _I_ _J_ _K_ _L_ _M_ _N_ _O_ _P_ _Q_ _R_ _S_ _T_ _U_ _V_ _W_ _X_ _Y_ _Z_ _AA_ _AB_ _AC_ _AD_ _AE_ _AF_ _AG_
   1 回数         1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1       1   1   1   1    1    1    2    1    1      回数
   2 日付     1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25   26   27   28   29   30   31     
   3 曜日     火  水  木  金  土  日  月  火  水  木                                                                                       木     
   4 氏名01   休          休          休          休          休          休          休          休          休            休             休     
       :   :   :

 <結果図>
  行  __________ AI __________  _AJ_  _AK_  _AL_  _AM_  _AN_
   1  回数                              1     1     1     1 
   2  日付                        1     2     3     4     5
   3                                                        
   4  氏名06  2回  10,13                                  
   5  氏名07  2回  25,28                                  
   6  氏名09  1回  3                                      
   7  氏名10  2回  4,11                                   
   8  氏名11  2回  17,23                                  
   9  氏名12  2回  2,5                                    
  10  氏名13  3回  12,16,27                               
  11  氏名14  2回  24,30                                  
  12  氏名15  4回  6,9,15,18                 3            
  13  氏名16  3回  7,19,28                         4      
  14  氏名17  3回  8,14,20                                
  15  氏名18  2回  26,29               2                 5
  16  氏名19  1回  22                                     

 実行マクロ名は「holidayExtract」

 ’シートモジュールにコピペするコード(一括貼り付け)
 ↓

 Option Explicit

 Private Const numOfRows As Long = 20 '対象人数

 Private DayTimesAry As Range
 Private RngSRC As Range
 Private RngDST As Range
 Private RngRowNum As Range
 Private RngRemaining As Range
 Private RngToSort As Range
 Private RngPreDay As Range

 Sub holidayExtract()
     Dim Cel As Range, i As Long, RW As Long, rwIndex As Long, NN As Long
     Dim 名簿Num, cntLeft As Long, KK As Long

     '環境設定
     Call init

     '作業範囲をクリアする
     Range("AI4:BV4").Resize(100).ClearContents

     'AI列に名簿番号を振り、BO列にランダムな背番号を振る
     Range("AI4").Resize(numOfRows).FormulaLocal = "=row()-3"
     Range("BQ4").Resize(numOfRows).FormulaLocal = "=rand()"
     Range("AI4:BQ4").Resize(numOfRows).Value = Range("AI4:BQ4").Resize(numOfRows).Value
     Range("AI1:BO2").Value = Range("A1:AG2").Value

     '回数を残回数に転記する
     RngRemaining.Value = Range("AG4").Resize(numOfRows).Value

     '作業表の3行目にチェック用数式を入力する
     Range("AJ3:BO3").FormulaR1C1 = _
     "=IF(ISNUMBER(R[-2]C),IF(COUNT(R[1]C:R[27]C)=R[-2]C,"""",""NG""),"""")"

     Range("BP3:BV3") = [{"前回日","ランダム背番号","","名簿番号","残回数","前回日","ランダム背番号"}]

     Application.ScreenUpdating = False
     '優先度→ 今後の選択肢数(昇順)>残回数(降順)>最終割当日(昇順)>名簿(ランダム)

     For i = 2 To 32
         rwIndex = 0  '各日の候補抽出数

         If DayTimesAry(1, i) > 0 Then
             For RW = 1 To numOfRows
                 If RngRemaining(RW, 1) > 0 Then
                     If isEligible(RngSRC(RW, i)) Then '適格性チェック
                         rwIndex = rwIndex + 1

                         RngToSort(rwIndex, 1) = RngDST.Cells(RW, 1).Value '名簿番号
                         RngToSort(rwIndex, 2) = RngDST.Cells(RW, 33).Value  '残回数

                         '選択肢の少ない方を優先させる為、今後の選択肢数を算出
                         cntLeft = 0

                         For KK = i To 32
                             If isEligible(RngSRC(RW, KK)) Then
                                 cntLeft = cntLeft + 1
                             End If
                         Next KK

                         RngToSort(rwIndex, 2) = RngToSort(rwIndex, 2) + 1000 - cntLeft

                         RngToSort(rwIndex, 3) = RngDST.Cells(RW, 34).Value  '前回日
                         RngToSort(rwIndex, 4) = RngDST.Cells(RW, 35).Value  'ランダム背番号
                     End If
                 End If
             Next RW
         End If

         If rwIndex > 0 Then 'ソートする
             ActiveSheet.Sort.SortFields.Clear

             ActiveSheet.Sort.SortFields.Add Key:=Range("BT4"), Order:=xlDescending
             ActiveSheet.Sort.SortFields.Add Key:=Range("BU4"), Order:=xlAscending
             ActiveSheet.Sort.SortFields.Add Key:=Range("BV4"), Order:=xlAscending

             With ActiveSheet.Sort
                 .SetRange RngToSort
                 .Header = xlNo
                 .Apply
             End With

             'ソート結果を反映させる

             For NN = 1 To Application.Min(DayTimesAry(1, i), rwIndex)
                 名簿Num = RngToSort(NN, 1) '名簿番号
                 RngDST(名簿Num, i) = i - 1
                 RngRemaining(名簿Num, 1) = RngRemaining(名簿Num, 1) - 1
                 RngPreDay(名簿Num, 1) = i - 1
             Next NN

             '更地化(次の列処理に備える)
             RngToSort.ClearContents
         End If
     Next i

     Application.ScreenUpdating = True

     If 未達Zecke > 0 Then
         Debug.Print "未達者あり"
        最終調整  '未達者が残った場合の追加処理
     Else
         Debug.Print "未達者無し"
         showResult
     End If
 End Sub

 Private Sub init()
     Set RngRemaining = Range("BO4").Resize(numOfRows)

     Set DayTimesAry = Range("A1:AF1")
     Set RngSRC = Range("A4:AG4").Resize(numOfRows)   '前後の列を含める
     Set RngDST = Range("AI4:BQ4").Resize(numOfRows)  '名簿番号列から前回日列まで含める
     Set RngToSort = Range("BS4:BV4").Resize(numOfRows)
     Set RngPreDay = Range("BP4").Resize(numOfRows)

 End Sub

 '抽出可否判定
 Private Function isEligible(cellToTest As Range) As Boolean
     If cellToTest = "日" Or (cellToTest = "休" And _
         Application.CountIf(cellToTest.Offset(, -1).Resize(1, 3), "休") = 1) Then
         isEligible = True
     End If
 End Function

 Private Function 未達Zecke() As Long '未達者チェック
     Dim RW As Long

     For RW = 1 To numOfRows
         If RngRemaining(RW, 1) > 0 Then
             未達Zecke = RW
             Exit For
         End If
     Next RW
 End Function

 Private Function 未達Day() As Long  '未達日チェック
     Dim CL As Long

     For CL = 1 To 31
         If DayTimesAry(1, CL + 1) > 0 Then
             If Application.Count(RngDST.Columns(CL + 1)) < DayTimesAry(1, CL + 1) Then
                 未達Day = CL
                 Exit For
             End If
         End If
     Next CL
 End Function

 Private Function getExchanged(未達Zecke, 未達Day) As Range
     Dim aRow As Range
     Dim Cel  As Range
     Dim RW   As Long, CL As Long
     Dim celToMove(1 To 2) As Range '確定を変更 1→2
     Dim celToNewAsign As Range

     For CL = 1 To 31
         If IsEmpty(RngDST(未達Zecke, CL + 1)) Then '未達者で割り当てられていない日をチェックして行く
             If isEligible(RngSRC(未達Zecke, CL + 1)) Then 'それが適格かどうかチェックする
                 Set celToMove(2) = RngDST.Cells(未達Zecke, CL + 1)

                 For RW = 1 To numOfRows  '当該列中に交換可能な他の名簿番号があるかチェックする
                     If RngRemaining(RW, 1) & "" = "0" Then
                         If RngDST(RW, CL + 1).Value Then  '交換可能な日付をチェック
                                 Set celToMove(1) = RngDST.Cells(RW, CL + 1)

                             If isEligible(RngSRC(RW, 未達Day + 1)) Then   '交換可能な日付なら
                                 Set celToNewAsign = RngDST(RW, 未達Day + 1)

                                 Debug.Print celToNewAsign.Address, celToMove(1).Address, celToMove(2).Address
                                 Union(celToNewAsign, celToMove(1), celToMove(2)).Select

                                 Stop
                                 celToNewAsign.Value = 未達Day
                                 celToMove(2) = celToMove(1).Value
                                 celToMove(1) = Empty

                                 RngRemaining(未達Zecke, 1) = RngRemaining(未達Zecke, 1) - 1
                                 Exit Function

                             End If
                         End If
                     End If
                 Next RW
             End If
         End If

     Next CL

 End Function

 Private Sub 最終調整()
     Dim 未達名簿 As Long, 未達日 As Long
     Dim RW As Long, CL As Long
     Dim Limit As Long

 '    Call init

     未達名簿 = 未達Zecke
     未達日 = 未達Day
     Do While 未達名簿 > 0 And Limit < 100
         If 未達名簿 > 0 Then
             Call getExchanged(未達名簿, 未達日)
         End If

         未達名簿 = 未達Zecke
         Limit = Limit + 1
         If Limit >= 100 Then
             MsgBox "トライリミットに達しました。再実行してください"
         End If
     Loop

     If 未達Day > 0 Then
         MsgBox "ありえない事態が生じました。処理中止"
         Exit Sub
     Else
         showResult
     End If

 End Sub

 Private Sub showResult()
     Dim RW As Long, CL As Long, Pos As Long
     Dim Result(), length As Long

     ReDim Result(1 To numOfRows, 1 To 1)
     length = [MAX(LEN(A1:A200))]

     For RW = 1 To numOfRows
         If RngRemaining(RW, 1) & "" = "0" Then
             Pos = Pos + 1
             Result(Pos, 1) = Left(RngSRC(RW, 1) & Space(15), length + 2) & Format(RngSRC(RW, 33), "##回  ")

             For CL = 1 To 31
                 If RngDST(RW, CL + 1) <> "" Then

                     Result(Pos, 1) = Result(Pos, 1) & "," & RngDST(RW, CL + 1)
                 End If
             Next CL

             Result(Pos, 1) = Replace(Result(Pos, 1), ",", "", 1, 1)
         End If

     Next RW

     Range("AI4").Resize(numOfRows) = Result
 End Sub

( 半平太) 2019/01/04(金) 19:25


······いやぁ、すごいのありがとうございます。
ただすみません、確認は明日になると思います。

一点質問があります。

 >実行マクロ名は「holidayExtract」

これはシートモジュールに貼り付けたマクロに名前を付けるということでしょうか。
普段記録をとって標準モジュールで編集してボタンで···くらいしかマクロ使わないので、シートモジュールに貼り付けるだけで大丈夫でしょうか。
(前) 2019/01/04(金) 21:44


 すみません。たまたま旨く行ったみたいです。

 >RngToSort(rwIndex, 2) = RngToSort(rwIndex, 2) + 1000 - cntLeft
             ↑
 ロジックとしては、これは間違いで、こっちが正しいんですが、
                   ↓
  RngToSort(rwIndex, 2) = RngToSort(rwIndex, 2) + (1000 - cntLeft) * 100

 そう修正すると収束しません。 ごめんなさい m(__)m

 >シートモジュールに貼り付けるだけで大丈夫でしょうか。

 大丈夫です。
 (※) 標準モジュールに貼り付けた場合は、そのシートをアクティブにして実行する必要があります。

 と言ったところで、上述の問題があるので使い物にならないかも知れません。
 更なる検討をする予定はありません。m(__)m

( 半平太) 2019/01/04(金) 22:24


いえいえ、そもそも題が題ですから。乱数って難しいんですね。やはり組み合わせなんですかね。本当にありがとうございました。
(前) 2019/01/04(金) 22:49

家のパソコンでやってみました。
確かに完全ではないものの、ランダムに振り分けることはできてます。
これをもとにすれば、だいぶ作業が楽になります。
ありがとうございました。

···ただ、会社のパソコンオフラインなんですよね···。入力ミス含めて何時間かかるんですかね···。
(前) 2019/01/05(土) 09:32


 >・・・ただ、会社のパソコンオフラインなんですよね・・・。入力ミス含めて何時間かかるんですかね・・・。

 その作業はちょっと待ってください。

 「最終調整」の処理がこんなにワークしないというのも、自分のセイながら腑に落ちないです。

 もうちょっとマシなロジックが組めないか、気を取り直して考えてみます。

( 半平太) 2019/01/05(土) 10:20


わざわざすみません。ありがとうございます。
(前) 2019/01/05(土) 10:35

以下の回答は、お望みのものとは違うと思いますが、
同様な勤務予定表を毎月作成していた経験からすると、条件を満たす解のない場合も想定できますので、手作業での作成をお勧めします。

ランダムに抽出というよりは、複数解のあるパズルを解くようなものですから、
縦横の残数を見ながら適当にやっても10分程度の作業時間じゃないですか。

〜〜〜〜〜〜〜〜〜 一応解いた証として解の一例を示しときます。

  f  10,13
  g  19,28
  h
  i  4
  j  7,16
  k  2,26
  l  5,29
  m  9,27,30
  n  18,24
  o  3,6,12,15
  p  22,25,28
  q  8,14,23
  r  11,17
  s  20
〜〜〜〜〜〜〜〜〜 用途を邪推すると
勤務予定に欠損が出た時の勤務者確保のための待機の割当(当番)ですかね。
そうならば、2名以上の場合も考慮しとかないといけないですが、別ルールがあるんでしょうね。

(チオチモリン) 2019/01/05(土) 13:48


 力業だけじゃ寂しいからわちきもちょっと書いてみますた。
でも、抽出するだけで、休が連続とかの細かいところはみていません。
一応、↓こんなんが出てきます。

 後は、ご自身で修正してください。(笑) すみません。m(__)m
q	1	2
o	2	23,24
p	2	16,19
i	2	5,6
j	1	7
k	2	16,17
m	2	26,27
f	3	26,27,28
r	2	16,20
n	1	12
s	1	14
a	1	16
h	1	23
g	2	26,28
b	1	26
d	1	29
c	1	30

 ' VBAによるメルセンヌツイスタ
Option Explicit
' システムを起動してからの時間をミリ秒単位で返す
' ht tp://msdn.microsoft.com/ja-jp/library/cc429827.aspx
Private Declare Function GetTickCount Lib "kernel32" () As Long
' メルセンヌツイスタのパラメータ(ダイナミッククリエーターの結果)
Private Const MTN = 644, MTM = 322, MTA = 12, MTB = 7, MTC = 15, MTD = 18
Private Const MXA = &H70C20000, UMK = &H78000000, LMK = &H7FFFFFF
Private Const MKB = &H73736B80, MKC = &H6ED28000
' 補助的な定数の宣言
Private Const MTL = MTN - MTM, MTK = MTN - 1, MTJ = MTL - 1, MTP = MTN - 2
Private Const PWA = 2 ^ MTA, PWB = 2 ^ MTB, PWC = 2 ^ MTC, PWD = 2 ^ MTD
Private Const KB = MKB \ PWB, KC = MKC \ PWC
Private Const P32 = 2# ^ 32, P31 = 2 ^ 31, P22 = 2# ^ 22, P9 = 2 ^ 9
Private Const M53 = 2# ^ -53, M32 = 2# ^ -32, M30 = 2# ^ -30
' 乱数の状態
Private mt(0 To MTK), mti As Long
' 初期化の補助関数
Private Function Ri(ByRef r As Double, ByVal i As Long) As Long
    Dim s As Variant
    Dim shft As Double
    Dim a As Long
    If r >= P31 Then a = r - P32 Else a = r
    a = a Xor Int(r * M30)
    If a < 0 Then r = a + P32 Else r = a
    s = 1812433253 * CDec(r) + i: r = s - CDec(Int(s * M32)) * P32
    If r >= P31 Then Ri = r - P31 Else Ri = r
End Function
' s を種にして乱数を初期化する
Public Sub InitMt(ByVal s As Long)
    Dim r As Double
    mt(0) = s And &H7FFFFFFF
    If s < 0 Then r = P32 + s Else r = s
    For mti = 1 To MTK: mt(mti) = Ri(r, mti): Next mti
    mti = MTN
End Sub
' 31 ビットの整数乱数
Public Function NextMt() As Long
    Dim y, k As Long
    If mti = 0 Then InitMt (1)
    If mti = MTN Then
        mti = 0
        For k = 0 To MTJ
            y = (mt(k) And UMK) Or (mt(k + 1) And LMK)
            mt(k) = mt(k + MTM) Xor (y \ 2) Xor (-(y And 1) And MXA)
        Next k
        For k = MTL To MTP
            y = (mt(k) And UMK) Or (mt(k + 1) And LMK)
            mt(k) = mt(k - MTL) Xor (y \ 2) Xor (-(y And 1) And MXA)
        Next k
        y = (mt(MTK) And UMK) Or (mt(0) And LMK)
        mt(MTK) = mt(MTM - 1) Xor (y \ 2) Xor (-(y And 1) And MXA)
    End If
    y = mt(mti): mti = mti + 1
    y = y Xor (y \ PWA): y = y Xor ((y And KB) * PWB)
    y = y Xor ((y And KC) * PWC): y = y Xor (y \ PWD): NextMt = y
End Function
' 0 以上 1 未満の乱数を返す
Public Function NextUnifMt() As Double
    Dim x As Long
    x = NextMt \ P9: NextUnifMt = (NextMt * P22 + x) * M53
End Function
' 時間を種にして乱数を初期化する
Public Sub RandomizeMt()
    InitMt (GetTickCount())
End Sub
Sub てすと()
Dim MyA As Variant
Dim MyB As Variant
Dim MyAry() As Variant
Dim 出勤日() As Variant
Dim MyDic As Object
Dim x As Variant
Dim xx() As Variant
Dim MyKeys As Variant
Dim MyItems As Variant
Dim nn As Variant
Dim i As Long
Dim j As Long
Dim k As Long
Dim n As Long
Dim r As Long
Set MyDic = CreateObject("Scripting.Dictionary")
With Sheets("Sheet1")
    With .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
        MyA = .Resize(, 32).Value
        MyB = .Offset(3).Resize(.Rows.Count - 3, 32).Value
    End With
End With
k = 0
For j = LBound(MyA, 2) To UBound(MyA, 2)
    'おっさんなんべんすんねん ( ̄▽ ̄;)
    これくしょんでしゃっふるぅぅぅ MyB
    r = 0
    If MyA(1, j) > 0 Then
        n = Application.CountA(Application.Index(MyB, 0, j))
        If MyA(1, j) <= n Then
            For i = LBound(MyB, 1) To UBound(MyB, 1)
                If MyB(i, j) <> "" Then
                    r = r + 1
                    If r > MyA(1, j) Then Exit For
                    k = k + 1
                    ReDim Preserve MyAry(1 To 3, 1 To k)
                    '名前
                    MyAry(1, k) = MyB(i, 1)
                    '日付
                    MyAry(3, k) = MyA(2, j)
                End If
            Next
        End If
    End If
Next
For j = LBound(MyAry, 2) To UBound(MyAry, 2)
    If Not MyDic.Exists(MyAry(1, j)) Then
        ReDim x(1)
        k = x(1)
        k = k + 1
        ReDim Preserve xx(k)
        xx(k) = MyAry(3, j)
        x(1) = xx
        MyDic(MyAry(1, j)) = x
    Else
        MyItems = MyDic(MyAry(1, j))
        k = UBound(MyItems(1)) + 1
        ReDim Preserve xx(k)
        xx(k) = MyAry(3, j)
        x(1) = xx
        MyDic(MyAry(1, j)) = x
    End If
Next
MyItems = MyDic.Items
MyKeys = MyDic.Keys
For i = LBound(MyItems) To UBound(MyItems)
    k = 0
    nn = MyItems(i)
    For j = LBound(nn(1)) To UBound(nn(1))
        If nn(1)(j) <> Empty Then
            ReDim Preserve 出勤日(k)
            出勤日(k) = nn(1)(j)
            k = k + 1
        End If
    Next
    x = MyDic(MyKeys(i))
    x(1) = Join(出勤日, ",")
    x(0) = UBound(Split(x(1), ",")) + 1
    MyDic(MyKeys(i)) = x
Next
MyItems = MyDic.Items
MyKeys = MyDic.Keys
ReDim MyAry(1 To MyDic.Count, 1 To 3)
For i = LBound(MyAry, 1) To UBound(MyAry, 1)
    MyAry(i, 1) = MyKeys(i - 1)
    MyAry(i, 2) = MyItems(i - 1)(0)
    MyAry(i, 3) = MyItems(i - 1)(1)
Next
With Sheets("Sheet2")
    .Cells.Clear
    .Range("A1").Resize(UBound(MyAry, 1), UBound(MyAry, 2)).Value = MyAry
    .Range("A1").CurrentRegion.EntireColumn.AutoFit
End With
Set MyDic = Nothing
Erase MyA, MyB, MyAry, 出勤日, x, xx, MyKeys, MyItems, nn
MsgBox "処理が完了しました"
End Sub
Private Sub これくしょんでしゃっふるぅぅぅ(ByRef x As Variant)
Dim y As Variant
Dim i As Long
Dim j As Long
Dim MyScs As Object
    Set MyScs = CreateObject("System.Collections.SortedList")
    ReDim y(LBound(x, 1) To UBound(x, 1), LBound(x, 2) To UBound(x, 2))
    RandomizeMt
    For i = LBound(x, 1) To UBound(x, 1)
        MyScs(NextUnifMt()) = i
    Next
    For i = 0 To MyScs.Count - 1
        For j = LBound(x, 2) To UBound(x, 2)
            y(i + 1, j) = x(MyScs.GetByIndex(i), j)
        Next
    Next
    x = y
    Set MyScs = Nothing
    Erase y
End Sub
v(=∩_∩=)v
※今からちょっとお馬ちゃんモードに入ります。すみません。当てるどぉ〜〜〜〜!!!!
(SoulMan) 2019/01/05(土) 13:59

 1.「最終調整」の処理はすごく面倒だし、それで解決する保証もないので、取りやめます。

   おかげでコード量が減ります。

 2.代わって「残りの日全体を見渡して、一人しか担当できない日が生じたら、その人を先に割り当てる」と言うロジックを追加します。

   これは、結構いいアイデアだった。一発で成功しました。(たまたまじゃないことを祈る)

 3.もう一つ、「一回目のトライでマズい場合は、マズかった人を記憶しておいて、2回目はその人を優先処理する」
   と言うのも思いついたのですが、他の人の回答もついているので、こっちアイデアは保留します。

 成功結果は、AI列の4行目以下に表示されます。

 コピペするコード

 Option Explicit

 Private Const numOfRows As Long = 20 '対象人数

 Private DayTimesAry As Range
 Private RngSRC As Range
 Private RngDST As Range
 Private RngRowNum As Range
 Private RngRemaining As Range
 Private RngToSort As Range
 Private RngPreDay As Range

 Sub holidayExtract()
     Dim Cel As Range, i As Long, RW As Long, rwIndex As Long, NN As Long
     Dim 名簿Num, cntLeft As Long, KK As Long

     '環境設定
     Call init

     '作業範囲をクリアする
     Range("AI4:BV4").Resize(100).ClearContents

     'AI列に名簿番号を振り、BO列にランダムな背番号を振る
     Range("AI4").Resize(numOfRows).FormulaLocal = "=row()-3"
     Range("BQ4").Resize(numOfRows).FormulaLocal = "=rand()"
     Range("AI4:BQ4").Resize(numOfRows).Value = Range("AI4:BQ4").Resize(numOfRows).Value
     Range("AI1:BO2").Value = Range("A1:AG2").Value

     '残回数の算出数式を入力する
     Range("BO4").Resize(numOfRows).FormulaR1C1 = _
         "=IF(RC[-34]="""","""",RC[-34]-COUNT(RC[-31]:RC[-1]))"

     '作業表の3行目にチェック用数式を入力する
     Range("AJ3:BO3").FormulaR1C1 = _
         "=IF(ISNUMBER(R[-2]C),IF(COUNT(R[1]C:R[27]C)=R[-2]C,"""",""NG""),"""")"

     Range("BP3:BV3") = [{"前回日","ランダム背番号","","名簿番号","残回数","前回日","ランダム背番号"}]

     Application.ScreenUpdating = False
     '優先度→ 今後の選択肢数(昇順)>残回数(降順)>最終割当日(昇順)>名簿(ランダム)

     For i = 2 To 32
         '各日に選択肢が一つしかない人を優先処理

         firstPriorityProc

         rwIndex = 0  '各日の候補抽出数

         If DayTimesAry(1, i) > 0 Then
             For RW = 1 To numOfRows
                 If IsEmpty(RngDST(RW, i)) And CStr(RngRemaining(RW, 1)) > "0" Then
                     If isEligible(RngSRC(RW, i)) Then '適格性チェック
                         rwIndex = rwIndex + 1

                         RngToSort(rwIndex, 1) = RngDST.Cells(RW, 1).Value '名簿番号
                         RngToSort(rwIndex, 2) = RngDST.Cells(RW, 33).Value  '残回数

                         '選択肢の少ない方を優先させる為、今後の選択肢数を算出
                         cntLeft = 0

                         For KK = i To 32
                             If isEligible(RngSRC(RW, KK)) Then
                                 cntLeft = cntLeft + 1
                             End If
                         Next KK

 '                        RngToSort(rwIndex, 2) = RngToSort(rwIndex, 2) + 1000 - cntLeft
                         RngToSort(rwIndex, 2) = RngToSort(rwIndex, 2) + (1000 - cntLeft) * 100

                         RngToSort(rwIndex, 3) = RngDST.Cells(RW, 34).Value * 1     '前回日
                         RngToSort(rwIndex, 4) = RngDST.Cells(RW, 35).Value  'ランダム背番号
                     End If
                 End If
             Next RW
         End If

         If rwIndex > 0 Then 'ソートする
             ActiveSheet.Sort.SortFields.Clear

             ActiveSheet.Sort.SortFields.Add Key:=Range("BT4"), Order:=xlDescending
             ActiveSheet.Sort.SortFields.Add Key:=Range("BU4"), Order:=xlAscending
             ActiveSheet.Sort.SortFields.Add Key:=Range("BV4"), Order:=xlAscending

             With ActiveSheet.Sort
                 .SetRange RngToSort
                 .Header = xlNo
                 .Apply
             End With

             'ソート結果を反映させる

             For NN = 1 To Application.Min(DayTimesAry(1, i), rwIndex)
                 名簿Num = RngToSort(NN, 1) '名簿番号
                 RngDST(名簿Num, i) = i - 1
                 RngPreDay(名簿Num, 1) = i - 1
             Next NN

             '更地化(次の列処理に備える)
             RngToSort.ClearContents
         End If
     Next i

     Application.ScreenUpdating = True

     '結果表示
     If [AND(SUM((BO4:BO100<>0)*(BO4:BO100<>""))=0,COUNTBLANK(AJ3:BN3)=31)] Then
         showResult
     Else
         MsgBox "不成功でした。再トライしてみてください"
     End If

 End Sub

 Private Sub init()
     Set RngRemaining = Range("BO4").Resize(numOfRows)

     Set DayTimesAry = Range("A1:AF1")
     Set RngSRC = Range("A4:AG4").Resize(numOfRows)   '前後の列を含める
     Set RngDST = Range("AI4:BQ4").Resize(numOfRows)  '名簿番号列から前回日列まで含める
     Set RngToSort = Range("BS4:BV4").Resize(numOfRows)
     Set RngPreDay = Range("BP4").Resize(numOfRows)

 End Sub

 '抽出可否判定
 Private Function isEligible(cellToTest As Range) As Boolean
     If cellToTest = "日" Or (cellToTest = "休" And _
         Application.CountIf(cellToTest.Offset(, -1).Resize(1, 3), "休") = 1) Then
         isEligible = True
     End If
 End Function

 Private Sub firstPriorityProc()
     Dim RW As Long, CL As Long
     Dim Choice(1 To 2) As Long

     For CL = 1 To 31
         If Range("AJ3:BN3")(1, CL) = "NG" Then
             For RW = 1 To numOfRows
                 If CStr(RngRemaining(RW, 1)) > "0" Then
                     If isEligible(RngSRC(RW, CL + 1)) Then
                         Choice(1) = Choice(1) + 1
                         Choice(2) = RW
                     End If
                 End If
             Next
         End If

         If Choice(1) = 1 Then
             RngDST(Choice(2), CL + 1) = CL
         End If
         Erase Choice
     Next CL
 End Sub

 Private Sub showResult()
     Dim RW As Long, CL As Long, Pos As Long
     Dim Result(), length As Long

     ReDim Result(1 To numOfRows, 1 To 1)
     length = [MAX(LEN(A1:A200))]

     For RW = 1 To numOfRows
         If CStr(RngRemaining(RW, 1)) = "0" Then
             Pos = Pos + 1
             Result(Pos, 1) = Left(RngSRC(RW, 1) & Space(15), length + 2) & Format(RngSRC(RW, 33), "##回  ")

             For CL = 1 To 31
                 If RngDST(RW, CL + 1) <> "" Then

                     Result(Pos, 1) = Result(Pos, 1) & "," & RngDST(RW, CL + 1)
                 End If
             Next CL

             Result(Pos, 1) = Replace(Result(Pos, 1), ",", "", 1, 1)
         End If

     Next RW

     Range("AI4").Resize(numOfRows) = Result
 End Sub

( 半平太) 2019/01/05(土) 15:23


ありがとうございます。どうしても28日がNGになりますが、1日だけですし問題にはなりません。チオチモリンさんもおっしゃるとおり後は人の手で出来ることです。
半平太さん、SoulManさん、チオチモリンさんありがとうございました。
(前) 2019/01/05(土) 16:34

 >どうしても28日がNGになりますが

 こちらに提示したデータと違うんじゃないですか?

 極端な話
   二人が各1回しか出られなくて、
   しかも同日にしか出られなくて、
   その日は1人だけしか必要ない

 そんな場合、絶対に成立しませんけど?
( 半平太) 2019/01/05(土) 16:46

確かに別の月のデータを使用してました。すみません。
”休”は1人10回前後必ずあるので、そのような場合は起こらないです。ありがとうございました。
(前) 2019/01/05(土) 18:13

解決したようで何よりです。

私は乱数設定による力づく方法をチューニングしていました。
というか別の処理系のコーディング練習の題材にさせていただいていました。ペコリ。
大分慣れることができました。

100万個のケースを試行させると、17個ほど見つかりました。(ちょっと見、それぞれ違うようです)。
時間は10秒程度でした。

== 17 回目の成功 ==========

 a[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 b[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 c[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 d[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 e[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 f[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 g[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0]
 h[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 i[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 j[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
 k[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 l[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0]
 m[0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
 n[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
 o[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
 p[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
 q[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 r[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 s[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
  10.494692 seconds (65.93 M allocations: 9.433 GiB, 11.78% gc time)

コードは、地道に、各日の選択肢をランダムに選択し、横計が目標値に一致するかどうかを確認しています。
なお、最初に、
各日の必要数と、設定可能者の合計が同じところは選択を確定させたり、
日別の必要数がゼロのところは、最初から省いて計算効率を上げるようにしました。
あいにく、VBAではないので提示できませんが。
(数値を差し替えました)

(γ) 2019/01/05(土) 23:37


 >どうしても28日がNGになりますが、1日だけです
    :  :   :
 >別の月のデータを使用してました

 でしたら、以下のシフト表を作ってテストしてみたらどうですかね。

 (1) 28日の2回は無しにする。
 (2) 28日に出られる人を2人選んで、その人達の出番を各1回減らす。

 そのテストで旨く行けば、完成結果に上の二つを手動で加味する。

 旨く行かず、こんどは別の日がNGになれば、解が無いっぽいです。

 そのテストデータを提示して、γさんの処理系プログラムに掛けたらどうなるか
 ・・なんて考えると、すごく興味が湧いてきます・・

( 半平太) 2019/01/05(土) 23:51


 だいぶ遅い時間ですが。
 暇を見つけながら会社のPCに入力してました。時間はかかりましたが家のPCで使った単純なシフト表と同じ結果が出たので多分コピペ出来たと思います(思い込みたい)

 >というか別の処理系のコーディング練習の題材にさせていただいていました。ペコリ。

 知らないところでYさんの役に立ってたようで。こちらこそアドバイスありがとうございました。m(_ _)m

 >旨く行かず、こんどは別の日がNGになれば、解が無いっぽいです。

 数カ月分のデータを使って半平太さんの方法をやってみましたが、やはり必ず1、2日はNGの日が出ます。半平太さんの予想通り解が無いっぽいです。

 そのデータですが、シフト表データを入力する時間(気力も)がないため提示できないです。パソコン繋がってたら簡単なんですけどね……。

 自分勝手で申し訳ないです。長い間本当にありがとうございました。

(前) 2019/01/07(月) 01:41


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.