mirror of
https://github.com/golang/go
synced 2024-11-22 12:44:50 -07:00
225 lines
4.6 KiB
Go
225 lines
4.6 KiB
Go
|
/*
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions are met:
|
||
|
|
||
|
* Redistributions of source code must retain the above copyright
|
||
|
notice, this list of conditions and the following disclaimer.
|
||
|
|
||
|
* Redistributions in binary form must reproduce the above copyright
|
||
|
notice, this list of conditions and the following disclaimer in the
|
||
|
documentation and/or other materials provided with the distribution.
|
||
|
|
||
|
* Neither the name of "The Computer Language Benchmarks Game" nor the
|
||
|
name of "The Computer Language Shootout Benchmarks" nor the names of
|
||
|
its contributors may be used to endorse or promote products derived
|
||
|
from this software without specific prior written permission.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* The Computer Language Benchmarks Game
|
||
|
* http://shootout.alioth.debian.org/
|
||
|
*
|
||
|
* contributed by The Go Authors.
|
||
|
* Based on fannkuch.scala by Rex Kerr
|
||
|
*/
|
||
|
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"flag"
|
||
|
"fmt"
|
||
|
"runtime"
|
||
|
)
|
||
|
|
||
|
var n = flag.Int("n", 7, "count")
|
||
|
var nCPU = flag.Int("ncpu", 2, "number of cpus")
|
||
|
|
||
|
type Job struct {
|
||
|
start []int
|
||
|
n int
|
||
|
}
|
||
|
|
||
|
type Found struct {
|
||
|
who *Kucher
|
||
|
k int
|
||
|
}
|
||
|
|
||
|
type Kucher struct {
|
||
|
perm []int
|
||
|
temp []int
|
||
|
flip []int
|
||
|
in chan Job
|
||
|
}
|
||
|
|
||
|
func NewKucher(length int) *Kucher {
|
||
|
return &Kucher{
|
||
|
perm: make([]int, length),
|
||
|
temp: make([]int, length),
|
||
|
flip: make([]int, length),
|
||
|
in: make(chan Job),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (k *Kucher) permute(n int) bool {
|
||
|
i := 0
|
||
|
for ; i < n-1 && k.flip[i] == 0; i++ {
|
||
|
t := k.perm[0]
|
||
|
j := 0
|
||
|
for ; j <= i; j++ {
|
||
|
k.perm[j] = k.perm[j+1]
|
||
|
}
|
||
|
k.perm[j] = t
|
||
|
}
|
||
|
k.flip[i]--
|
||
|
for i > 0 {
|
||
|
i--
|
||
|
k.flip[i] = i
|
||
|
}
|
||
|
return k.flip[n-1] >= 0
|
||
|
}
|
||
|
|
||
|
func (k *Kucher) count() int {
|
||
|
K := 0
|
||
|
copy(k.temp, k.perm)
|
||
|
for k.temp[0] != 0 {
|
||
|
m := k.temp[0]
|
||
|
for i := 0; i < m; i++ {
|
||
|
k.temp[i], k.temp[m] = k.temp[m], k.temp[i]
|
||
|
m--
|
||
|
}
|
||
|
K++
|
||
|
}
|
||
|
return K
|
||
|
}
|
||
|
|
||
|
func (k *Kucher) Run(foreman chan<- Found) {
|
||
|
for job := range k.in {
|
||
|
verbose := 30
|
||
|
copy(k.perm, job.start)
|
||
|
for i, v := range k.perm {
|
||
|
if v != i {
|
||
|
verbose = 0
|
||
|
}
|
||
|
k.flip[i] = i
|
||
|
}
|
||
|
K := 0
|
||
|
for {
|
||
|
if verbose > 0 {
|
||
|
for _, p := range k.perm {
|
||
|
fmt.Print(p + 1)
|
||
|
}
|
||
|
fmt.Println()
|
||
|
verbose--
|
||
|
}
|
||
|
count := k.count()
|
||
|
if count > K {
|
||
|
K = count
|
||
|
}
|
||
|
if !k.permute(job.n) {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
foreman <- Found{k, K}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type Fanner struct {
|
||
|
jobind int
|
||
|
jobsdone int
|
||
|
k int
|
||
|
jobs []Job
|
||
|
workers []*Kucher
|
||
|
in chan Found
|
||
|
result chan int
|
||
|
}
|
||
|
|
||
|
func NewFanner(jobs []Job, workers []*Kucher) *Fanner {
|
||
|
return &Fanner{
|
||
|
jobs: jobs, workers: workers,
|
||
|
in: make(chan Found),
|
||
|
result: make(chan int),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (f *Fanner) Run(N int) {
|
||
|
for msg := range f.in {
|
||
|
if msg.k > f.k {
|
||
|
f.k = msg.k
|
||
|
}
|
||
|
if msg.k >= 0 {
|
||
|
f.jobsdone++
|
||
|
}
|
||
|
if f.jobind < len(f.jobs) {
|
||
|
msg.who.in <- f.jobs[f.jobind]
|
||
|
f.jobind++
|
||
|
} else if f.jobsdone == len(f.jobs) {
|
||
|
f.result <- f.k
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func swapped(a []int, i, j int) []int {
|
||
|
b := make([]int, len(a))
|
||
|
copy(b, a)
|
||
|
b[i], b[j] = a[j], a[i]
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
flag.Parse()
|
||
|
runtime.GOMAXPROCS(*nCPU)
|
||
|
N := *n
|
||
|
base := make([]int, N)
|
||
|
for i := range base {
|
||
|
base[i] = i
|
||
|
}
|
||
|
|
||
|
njobs := 1
|
||
|
if N > 8 {
|
||
|
njobs += (N*(N-1))/2 - 28 // njobs = 1 + sum(8..N-1) = 1 + sum(1..N-1) - sum(1..7)
|
||
|
}
|
||
|
jobs := make([]Job, njobs)
|
||
|
jobsind := 0
|
||
|
|
||
|
firstN := N
|
||
|
if firstN > 8 {
|
||
|
firstN = 8
|
||
|
}
|
||
|
jobs[jobsind] = Job{base, firstN}
|
||
|
jobsind++
|
||
|
for i := N - 1; i >= 8; i-- {
|
||
|
for j := 0; j < i; j++ {
|
||
|
jobs[jobsind] = Job{swapped(base, i, j), i}
|
||
|
jobsind++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nworkers := *nCPU
|
||
|
if njobs < nworkers {
|
||
|
nworkers = njobs
|
||
|
}
|
||
|
workers := make([]*Kucher, nworkers)
|
||
|
foreman := NewFanner(jobs, workers)
|
||
|
go foreman.Run(N)
|
||
|
for i := range workers {
|
||
|
k := NewKucher(N)
|
||
|
workers[i] = k
|
||
|
go k.Run(foreman.in)
|
||
|
foreman.in <- Found{k, -1}
|
||
|
}
|
||
|
fmt.Printf("Pfannkuchen(%d) = %d\n", N, <-foreman.result)
|
||
|
}
|