プリクラ問題

covering designのt=2に相当、
La Jolla Covering Repository Tablesに全部載ってます…
リンク先クリックすればいいんだね。
Combinatorial covering designs に文献とかあるから、なんかもう・・・
もういいや

fortran90によるのコードはこのページの下の方です
だんだんと条件を付け加えて更新していくつもりです。n=9~10でm=3,4程度は厳しいです。



力技でやってみたよ!
主眼が回数になっているので、実際の組み合わせは?と気になってやったものです。

事の発端

コンピュータによる総当たりでは今のままではn=10位が限界です。
だんだんと改良して増やせていければ、と。
コードは後ほど僕に時間ができ次第、公開します。



より↓

念のための確認もかねて、計算は表の値-1から確かめて総当たりを行った結果です。なので具体的な組み合わせが載っているものは正しいはずです。
組み合わせの総数はおおよそ
\(\displaystyle \sum_{q=3}^{q^{Answer}} {}_{{}_nC_m}C_{q}\)
という膨大な数の組み合わせとなります。ここで\(q^{Answer}\)はプリクラ問題の最小回数です。
\(q=3\)はあり得ない、全てを検証する必要はない、など確実にわかる要素は多いですが、例えばn=9,m=3,解q=12を考えると
\(\displaystyle {}_{{}_9C_3}C_{12}=112,992,892,764,570 \ \ \sim 10^{14}(=100T)\)回
の計算が必要です。理想的な状況を考え、cpu10GHz,コア数10で並列処理で行う場合、計算時間は
\(\displaystyle \frac{10^{14}回}{10\times 10^{9}Hz\cdot 10\mbox{コア}}=1000[秒]\)
なのでぎりぎり計算できそうかなーくらいになります。
しかし、このあたりが限界で、n=10,m=3,解q=17になると、
\(\displaystyle {}_{{}_{10}C_3}C_{17}=189,916,591,435,396,829,640\ \ \sim 10^{20}(100E)\)回
の計算となります。先ほどの設定で行うならば、\(10^{9}\)秒~31.7年かかります。
スパコンの京(10PHz)をフルに使えるならば1000[s]で終わります。しかしそこで終わりです。n=11は…もう無理です。

効率のいい方法を考えない限り、解は得られません。

n\m 2 3 4 5 6 7 8 9 10 11
2 1 1 1 1 1 1 1 1 1 1
3 3 1 1 1 1 1 1 1 1 1
4 6 3 1 1 1 1 1 1 1 1
5 10 4 3 1 1 1 1 1 1 1
6 15 6 3 3 1 1 1 1 1 1
7 21 7 5 3 3 1 1 1 1 1
8 28 11 6 4 3 3 1 1 1 1
9 36 12 8 5 3 3 3 1 1 1
10 45 17 9 6 4 3 3 3 1 1
11 55 19 11 7 6 4 3 3 3 1

(3,2)=3

1 2
1 3
2 3

(4,2)=6

(4,3)=3

1 2 3
1 2 4
1 3 4

(5,2)=10

(5,3)=4

1 2 3
1 2 4
1 2 5
3 4 5

(5,4)=3

1 2 3 4
1 2 3 5
1 2 4 5

(6,2)=15

(6,3)=6

1 2 3
1 2 4
1 5 6
2 5 6
3 4 5
3 4 6

(6,4)=3

1 2 3 4
1 2 5 6
3 4 5 6

(6,5)=3

1 2 3 4 5
1 2 3 4 6
1 2 3 5 6

(7,2)=21

(7,3)=7

1 2 3
1 4 5
1 6 7
2 4 6
2 5 7
3 4 7
3 5 6

(7,4)=5

1 2 3 4
1 2 3 5
1 2 3 6
1 2 3 7
4 5 6 7
or
1 2 3 5
1 4 6 7
2 3 4 5
2 4 6 7
3 5 6 7

(7,5)=3

1 2 3 4 5
1 2 3 6 7
1 4 5 6 7

(7,6)=3

1 2 3 4 5 6
1 2 3 4 5 7
1 2 3 4 6 7

(8,2)=28

(8,3)=11

計算中…

(8,4)=6

1 2 3 4
1 2 5 6
1 2 7 8
3 4 5 6
3 4 7 8
5 6 7 8

(8,5)=4

1 2 3 4 5
1 2 3 4 6
1 2 3 7 8
4 5 6 7 8

(8,6)=3

1 2 3 4 5 6
1 2 3 4 7 8
1 2 5 6 7 8

(8,7)=3

1 2 3 4 5 6 7
1 2 3 4 5 6 8
1 2 3 4 5 7 8

(9,2)=36

(9,3)=12

計算中…

(9,4)=8

1 2 3 5
1 2 3 6
1 2 4 7
1 2 8 9
1 3 4 8
1 3 7 9
4 5 6 9
5 6 7 8


info)cputime=3.0415[s], Nc=10523209, com=1 2 35 115 126

(9,5)=5

1 2 3 4 5
1 2 3 4 6
1 2 7 8 9
3 4 7 8 9
5 6 7 8 9

(9,6)=3

1 2 3 4 5 6
1 2 3 7 8 9
4 5 6 7 8 9

(9,7)=3

1 2 3 4 5 6 7
1 2 3 4 5 8 9
1 2 3 6 7 8 9

(9,8)=3

1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 9
1 2 3 4 5 6 8 9

(10,2)=45

(10,3)=17

計算厳しい

(10,4)=9

計算中(4日後くらい?)

(10,5)=6

(10,6)=4

1 2 3 4 5 6
1 2 3 4 7 8
1 2 3 4 9 10
5 6 7 8 9 10

(10,7)=3

1 2 3 4 5 6 7
1 2 3 4 8 9 10
1 5 6 7 8 9 10

(10,8)=3

1 2 3 4 5 6 7 8
1 2 3 4 5 6 9 10
1 2 3 4 7 8 9 10

(10,9)=3

1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 10
1 2 3 4 5 6 7 9 10

(11,2)=55

(11,3)=19

(11,4)=11

(11,5)=7

(11,6)=

(11,7)=4

1 2 3 4 5 6 7
1 2 3 4 5 6 8
1 2 3 4 9 10 11
5 6 7 8 9 10 11

(11,8)=3

1 2 3 4 5 6 7 8
1 2 3 4 5 9 10 11
1 2 6 7 8 9 10 11

(11,9)=3

1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 10 11
1 2 3 4 5 8 9 10 11

(11,10)=3

1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 11
1 2 3 4 5 6 7 8 10 11



fortran90のコード。総当たりで調べます。
別にinputファイルが必要です。n:m=3:2以上の時は考慮していません。3回で終わるからね。
下の例はn=6, m=3に相当しています。(inputのrはmに相当します。)
qiは調べ始める最低回数、qeは調べ終わる最高回数です。
出力はこうなります。

 M          35
q  -->    3
q  -->    4
q  -->    5
q  -->    6
q  -->    7
 number of calculation     1276464
 cpu_time  0.905050993     [sec]
+------------+
  1  2  3
  1  4  5
  1  6  7
  2  4  6
  2  5  7
  3  4  7
  3  5  6
+------------+

ここからfortran90のコード。gfortranで確かめています。

program main
  implicit none
  integer::n,r,qi,qe,qc
  integer::i,j
  real::t1,t2
  integer,allocatable::groups(:,:)
 
  n=7
  r=3
  qi=3
  qe=10
  allocate(groups(1:qe,1:r))
  groups=0

  call cpu_time(t1)  
  call pkprob(n,r,qi,qe,qc,groups)
  call cpu_time(t2)
  write(6,*)"cpu_time",t2-t1,"[sec]"

  write(6,'(A)')"+------------+"
  do i=1,qc
     do j=1,r
        write(6,'(i3)',advance='no')groups(i,j)
     enddo
     write(6,*)          
  enddo
  write(6,'(A)')"+------------+"
 
  stop
end program main
!==========================
subroutine pkprob(n,r,qi,qe,qc,groups)
  !Covering Design t=2
  implicit none
  integer,intent(in)::n,r,qi,qe
  integer,intent(out)::qc,groups(1:qe,1:r)
 
  integer::q,M,ic,ncalc
  integer::i,j,q2,k
  integer,allocatable::ini(:),tmp(:),cgroup(:,:),pk(:,:)
  integer,allocatable::choose(:)
 
  integer::check1,nCr
  integer::csign
  !  logical::csign,gsign
  external::nCr

  !Combination M=nCr
  M=nCr(n,r)  
  write(6,*)"M",M

  !csign      : Sign of critical combination of array  
  !ic         : Critical value of i which number of combination of combination
  !qc         : Critical value of q which number of combination of combination
  !pk(1:n,1:n): Checking array if every number fill(1) combination or not(0)
  !ncount(1:n): To check calculation number of times
  allocate(ini(1:r),tmp(1:r),cgroup(1:M,1:r))
  ini(1:r)=0
  cgroup(1:M,1:r)=0
  tmp(1:r)=0
 
  do j=1,r
     ini(j)=j
     tmp(j)=ini(j)
  enddo
  !Substitute combination to cgroup.
  do i=1,M
     cgroup(i,1:r)=tmp(1:r)
     call combination(n,r,ini,tmp)
  enddo
  deallocate(ini,tmp)
  !csign      : Sign of critical combination of array  
  !ic         : Critical value of i which number of combination of combination
  !qc         : Critical value of q which number of combination of combination
  !pk(1:n,1:n): Checking array if every number fill(1) combination or not(0)
  !ncount(1:n): To check calculation number of times
  csign=0
  ic=0
  qc=0
  allocate(pk(1:n,1:n))
  pk=0

  ncalc=0

  !q : We need to consider below combination.
  !  Example ) n=5, r=3 case.
  !
  !cgroup(i,1:r)
  !i\r| 1 2 3
  !----------
  ! 1 | 1 2 3
  ! 2 | 1 2 4
  ! 3 | 1 2 5
  ! 4 | 1 3 4
  ! 5 | 1 3 5
  ! 6 | 1 4 5
  ! 7 | 2 3 4
  ! 8 | 2 3 5
  ! 9 | 2 4 5
  !10 | 3 4 5
  !
  !We have to choose q= 3 or 4 or 5 ...combination of "cgroup".
  !Sum total of these combination can write D=_{nCr}C_q.
  !Specifically,
  !---choose q groups from cgroup
  ! +----q=3----+ +-----q=4-----+ +------q=5------+
  ! |choose(1:q)| | choose(1:q) | |  choose(1:q)  |
  ! | l |       | | l |         | | l |           |
  ! |===|=======| |===|=========| |===|===========|
  ! | 1 | 1 2 3 | | 1 | 1 2 3 4 | | 1 | 1 2 3 4 5 |
  ! | 2 | 1 2 4 | | 2 | 1 2 3 5 | | 2 | 1 2 3 4 6 |
  ! | 3 | 1 2 5 | | 3 | 1 2 3 6 | | 3 | 1 2 3 4 7 |
  ! | 4 | 1 2 6 | | 4 | 1 2 4 7 | | 4 | 1 2 3 4 8 |
  ! | 5 | 1 2 7 | | 5 | 1 2 4 8 | | 5 | 1 2 3 4 9 |
  ! | 6 | 1 2 8 | | 6 | 1 2 5 9 | | 6 | 1 2 3 4 10|
  ! | 7 | 1 2 9 | | 7 | 1 2 5 10| | 7 | 1 2 3 5 6 |
  ! |...| . . . | |...| . . . . | |...| . . . .   |
  ! |120| 4 5 6 | |210| 7 8 9 10| |252| 6 7 8 9 10|
  ! +-----------+ +-------------+ +---------------+
  !
  ! If q=4, l=3
  !  We choose below combination, and check satisfy condition or not by "pk".
  ! 1 | 1 2 3
  ! 2 | 1 2 4
  ! 3 | 1 2 5
  ! 6 | 1 4 5
 
  do q=qi,qe
     write(6,'(A,i5)')"q  -->",q
     allocate(choose(1:q),ini(1:q))
     choose(1:q)=0
     ini(1:q)=0
     
     do j=1,q
        ini(j)=j
        choose(j)=ini(j)
     enddo
         
     csign=0
     do while(csign.eq.0)
        !condition 1 to reduce calculation
        check1=cgroup(choose(1),1)
        if(check1.ne.1)exit

        !condition 2 to reduce calculation
        check1=cgroup(choose(2),1)
        if(check1.ge.2)exit

        !condition 3 to reduce calculation for less than n:r=3:2
        check1=cgroup(choose(q),1)
        if(check1.ge.3)then
           pk=0
           do q2=1,q
              do j=1,r
                 do k=1,r
                    pk(cgroup(choose(q2),j),cgroup(choose(q2),k))=1
                 enddo
              enddo
           enddo
           !memory calculation number of times
           ncalc=ncalc+1
           if(minval(pk).eq.1)then
              csign=1
              qc=q
           endif

           if(csign.eq.1)exit
        endif

        !go forward choose(1:q)
        call combination(M,q,ini,choose)
     enddo
     
     if(csign.eq.1)exit
     deallocate(choose,ini)
  enddo
  write(6,*)"number of calculation",ncalc

  groups(1:qe,1:r)=0
  do i=1,qc
     do j=1,r
        groups(i,j)=cgroup(choose(i),j)
     enddo
  enddo
 
  return
end subroutine pkprob
!---------------------------------
subroutine combination(n,r,ini,arr)
  implicit none  
  integer(8)::i,n,r,ini(1:r),bef(1:r),arr(1:r)
  integer(8)::numx
  logical::key(1:r)
 
  bef(1:r)=arr(1:r)-ini(1:r)
  arr(1:r)=0
  key(1:r)=.false.

  numx=n-r
  do i=1,r
     if(bef(i).eq.numx)key(i)=.true.
  enddo

  do i=1,r-1
     if(key(i+1))then
        if(key(i))then
           if(i.ne.1)arr(i)=arr(i-1)      
        else
           arr(i)=bef(i)+1
        endif
     else
        arr(i)=bef(i)
     endif
  enddo
  if(key(r))then
     arr(r)=arr(r-1)
  else
     arr(r)=bef(r)+1
  endif
 
  arr(1:r)=arr(1:r)+ini(1:r)

  return
end subroutine combination
!----------------------------------
function nCr(n,r)
  implicit none
  integer(8)::n,r,i,r0,nCr

  r0=n-r
  if(r.le.r0)r0=r

  nCr=1
  do i=1,r0
     nCr=nCr*(n-r0+i)
     nCr=nCr/i
  enddo
 
  return
end function nCr

コメントを残す

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