使用Go自帶的container/ring.Ring模擬約瑟夫環

據說著名猶太歷史學家 Josephus有過以下的故事:在羅馬人佔領喬塔帕特後,39 個猶太人與Josephus及他的朋友躲到一個洞中,39個猶太人決定寧願死也不要被敵人抓到,於是決定了一個自殺方式,41個人排成一個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺,然後再由下一個重新報數,直到所有人都自殺身亡爲止。然而Josephus 和他的朋友並不想遵從。首先從一個人開始,越過k-2個人(因爲第一個人已經被越過),並殺掉第k個人。接着,再越過k-1個人,並殺掉第k個人。這個過程沿着圓圈一直進行,直到最終只剩下一個人留下,這個人就可以繼續活着。問題是,給定了和,一開始要站在什麼地方纔能避免被處決?Josephus要他的朋友先假裝遵從,他將朋友與自己安排在第16個與第31個位置,於是逃過了這場死亡遊戲。

----------------------------------------josephus.go------------------------------------------

package josephus

import (
   "container/ring"
   "fmt"
)

type Player struct {
   pos   int
   alive bool
}

type Josephus struct {
   ring        *ring.Ring
   count       int
   counter     int
   startPos    int
   orderToDie int
   remain      int
}

func New(count, orderToDie int) *Josephus {
   r := ring.New(count)
   for i := 1; i < count+1; i++ {
      r.Value = &Player{i, true}
      r = r.Next()
   }
   return &Josephus{
      ring:        r,
      count:       count,
      remain:      count,
      counter:     0,
      startPos:    1,
      orderToDie: orderToDie,
   }
}

// Show()方法展示死亡過程,並進行標記,不從結構中移除
func (this *Josephus) Show() {
   for i := 1; this.remain > this.orderToDie; i++ {
      fmt.Printf("\n開始第%d輪,共%d人\n", i, this.remain)
      this.ring.Do(func(item interface{}) {
         player := item.(*Player)
         if player.alive {
            this.counter++
         }
         if this.counter == this.orderToDie {
            player.alive = false
            this.counter = 0
            this.remain--
            fmt.Printf("player %d died \n", player.pos)
         }
      })
   }
}

func (this *Josephus) ShowRemain() {
   fmt.Println()
   this.ring.Do(func(item interface{}) {
      player := item.(*Player)
      if player.alive {
         fmt.Printf("存活 %v\n", item)
      }
   })
}

// Run()方法將死亡玩家和存活玩家拆分並返回
func (this *Josephus ) Run() (dead,lived *ring.Ring) {
   r:=this.ring
   for ; this.remain >= this.orderToDie; r=r.Next() {
      player := r.Value.(*Player)
      if player.alive {
         this.counter++
      }
      if this.counter == this.orderToDie {
         player.alive = false
         this.counter = 0
         this.remain--
         r=r.Prev()
         deadPlayer:=r.Unlink(1)
         // 注意Ring的New方法創建的對象至少有一個元素,爲避免這個多餘的空元素,直接指定
         if dead==nil{
            dead=deadPlayer
         }else{
            dead.Link(deadPlayer)
         }
         dead.Link(deadPlayer)
      }
   }
   lived=r
   return
}

-------------------------------josephus_test.go-----------------------------------------------

package josephus

import (
   "testing"
   "fmt"
)

func TestJosephus(t *testing.T) {
   j:=New(41,3)
   j.Show()
   j.ShowRemain()
   fmt.Println("----------------------------------")
   j2:=New(41,3)
   dead,lived:=j2.Run()
   fmt.Println("存活")
   lived.Do(func(item interface{}) {
      fmt.Printf("%d,",item.(*Player).pos)
   })
   fmt.Println("\n死亡")
   dead.Do(func(item interface{}) {
      fmt.Printf("%d,",item.(*Player).pos)
   })

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章