[Shootout-list] fannkuch.f90 update

Simon Geard simon@whiteowl.co.uk
Mon, 13 Dec 2004 23:12:27 +0000


This is a multi-part message in MIME format.
--------------050903060300020003010902
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Actually it's only a comment that I've changed but it does make it a lot 
clearer!

Simon Geard

--------------050903060300020003010902
Content-Type: text/plain;
 name="fannkuch.f90"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="fannkuch.f90"

! Fannkuch access function implementation
! Simon Geard, 1/12/04
!
! Building info.
! ==============
!
! Linux  - using the Intel Fortran90 compiler:
!
!          ifort fannkuch.f90 -O3 -static-libcxa -o fannkuch
!
! WinXP  - Compaq Visual Fortran 6.6c
!
!          f90 fannkuch.f90 /link /libpath:"d:\Program Files\Microsoft Visual Studio\df98\lib"
!
! Cygwin - g95 compiler
!
!          g95 fannkuch.f90 -O3 -o fannkuch.exe
!
!!$"Take a permutation of {1,...,n}, for example: {4,2,1,5,3}. Take the first element, here 4, and reverse the order of the first 4 elements: {5,1,2,4,3}. Repeat this until the first element is a 1, so flipping won't change anything more: {3,4,2,1,5}, {2,4,3,1,5}, {4,2,3,1,5}, {1,3,2,4,5}. Count the number of flips, here 5. Do this for all n! permutations, and record the maximum number of flips needed for any permutation. The conjecture is that this maximum count is approximated by n*log(n) when n goes to infinity.
!!$
!!$FANNKUCH is an abbreviation for the German word Pfannkuchen, or pancakes, in analogy to flipping pancakes."
!!$
!!$Correct output N = 7 is:
!!$
!!$Pfannkuchen(7) = 16
!!$
!!$
!!$Correct output N = 8 is:
!!$
!!$Pfannkuchen(8) = 22
!!$
!!$
!!$Correct output N = 9 is:
!!$
!!$Pfannkuchen(9) = 30
!!$
!!$
!!$Correct output N = 10 is:
!!$
!!$Pfannkuchen(10) = 38

program fannkuch
  implicit none
  integer count, pcount, max_pc, i, k, num
  character(len=8) argv
  integer, dimension(:), allocatable :: data, refData

  call getarg(1,argv)
  read(argv,*) num
  allocate(data(num))
  allocate(refData(num))
  data = (/ (i,i=1,num) /)
  refData = data

  count = 0
  max_pc = factorial(num)
  do i=1,max_pc
     count = max(count,countFlips(data))
     call nextPerm(data)
  end do

  print *,'number of flips ',count
contains

  recursive integer function factorial(n) result(if)
    integer, intent(in) :: n
    if (n == 1) then
       if = 1
    else
       if = n*factorial(n-1)
    end if
  end function factorial

  ! Reverse an array
  subroutine reverse(data)
    integer, dimension(:), intent(inout) :: data
    integer, dimension(size(data)) :: work
    integer i
    do i=1,size(data)
       work(i) = data(size(data)-i+1)
    end do
    data = work
  end subroutine reverse

  ! Count the number of flips in a permutation
  integer function countFlips(data)
    integer, dimension(:), intent(in) :: data
    integer, dimension(size(data)) :: work
    work = data
    countFlips = 0
    do
       if (work(1) <= 1) exit
       call reverse(work(1:work(1)))
       countFlips = countFlips + 1
    end do
  end function countFlips

  ! Get the next permutation
  subroutine nextPerm(data)
    integer, dimension(:), intent(inout) :: data
    integer, dimension(:), allocatable, save :: dcheck
    integer, save :: N = 0
    integer, save :: s, p
    integer :: i, j, index

    if (N == 0) then
       N = size(data)
       s = (N+1)*N/2
       p = factorial(N)
       allocate(dcheck(N))
    end if

    do
       ! Add one to the current number in modulo N
       index = N
       do
          if (data(index) < N) then
             data(index) = data(index) + 1
             exit
          end if
          data(index) = 1
          index = index-1
       end do

       ! Need to ensure the new number has each digit once. I'm checking the sum and prduct first
       ! because I it is much (4x) faster. Ideally the number would be correct by construction and
       ! no such test would be necessary.
       if (sum(data) == s .and. product(data) == p) then
          ! Now ensure each digit is distinct
          dcheck = (/ (0,i=1,N) /)
          do i=1,N
             do j=1,N
                if (data(j) == i) then
                   dcheck(i) = 1
                   exit
                end if
             end do
          end do
          if (product(dcheck) == 1) then
             exit
          end if
       end if
    end do
  end subroutine nextPerm

end program fannkuch

--------------050903060300020003010902--