เชื้อโรคนั้นไปไหน


21

บทนำ

คุณเป็นนักชีววิทยาศึกษารูปแบบการเคลื่อนไหวของแบคทีเรีย ทีมวิจัยของคุณมีกลุ่มของพวกเขาในจาน Petri และคุณกำลังบันทึกกิจกรรมของพวกเขา น่าเสียดายที่คุณมีเงินทุนไม่เพียงพอและไม่สามารถซื้อกล้องวิดีโอได้ดังนั้นคุณเพียงแค่ถ่ายภาพอาหารเป็นระยะ ๆ งานของคุณคือสร้างโปรแกรมที่ติดตามการเคลื่อนไหวของเชื้อโรคจากภาพเหล่านี้

อินพุต

อินพุตของคุณเป็นสองมิติสองแถวของตัวละครในรูปแบบที่เหมาะสมแสดงรูปภาพต่อเนื่องของจานเลี้ยงเชื้อ ในทั้งสองอาร์เรย์ตัวละครจะ.แทนพื้นที่ว่างและOแสดงถึงเชื้อโรค (คุณสามารถเลือกอักขระสองตัวที่แตกต่างกันได้หากคุณต้องการ) ยิ่งไปกว่านั้นอาร์เรย์ "after" ได้มาจากอาร์เรย์ "before" โดยการย้ายเชื้อโรคไปหนึ่งก้าวในหนึ่งในสี่ทิศทางที่สำคัญ โดยเฉพาะอย่างยิ่งอาร์เรย์มีรูปร่างเหมือนกัน เชื้อโรคเคลื่อนที่ไปพร้อมกันดังนั้นหนึ่งในนั้นอาจย้ายไปยังพื้นที่ที่มีเชื้อโรคอื่นอยู่แล้วหากมันเคลื่อนที่ออกนอกเส้นทาง มีการรับประกันว่าขอบเขตของอาร์เรย์ "ก่อน" มีช่องว่างเท่านั้นและมีอย่างน้อยหนึ่งเชื้อโรค ดังนั้นต่อไปนี้เป็นคู่ของอินพุตที่ถูกต้อง:

Before  After
......  ......
.O..O.  ....O.
.OO.O.  .OO.O.
......  ..O...

เอาท์พุต

เอาต์พุตของคุณเป็นอาร์เรย์ 2D เดียวของตัวละครในรูปแบบเดียวกับอินพุต มันได้มาจากอาร์เรย์ "ก่อน" โดยแทนที่เชื้อโรคเหล่านั้นที่ย้ายด้วยหนึ่งใน>^<vขึ้นอยู่กับทิศทางของการเคลื่อนไหว (คุณยังสามารถใช้อักขระที่แตกต่างกัน 4 ตัวที่นี่) อาจมีผลลัพธ์ที่เป็นไปได้หลายอย่าง แต่คุณจะต้องให้เพียงหนึ่งในนั้น ในตัวอย่างข้างต้นหนึ่งเอาต์พุตที่ถูกต้องที่เป็นไปได้คือ

......
.v..O.
.>v.O.
......

การเคลื่อนไหวที่ไม่จำเป็นได้รับอนุญาตในเอาต์พุตและเชื้อโรคสามารถสลับตำแหน่งได้ดังนั้นสิ่งต่อไปนี้จะถูกต้อง:

......
.v..v.
.>v.^.
......

กฎและการให้คะแนน

คุณสามารถเขียนโปรแกรมเต็มรูปแบบหรือฟังก์ชั่น จำนวนไบต์ต่ำสุดที่ชนะและช่องโหว่มาตรฐานไม่ได้รับอนุญาต

ฉันสนใจในอัลกอริธึมที่ค่อนข้างมีประสิทธิภาพ แต่ฉันไม่ต้องการห้ามการเดรัจฉานบังคับโดยสิ้นเชิง ด้วยเหตุนี้จึงมีโบนัส -75%สำหรับการแก้ไขกรณีทดสอบล่าสุดภายใน 10 นาทีบน CPU ที่ทันสมัย ​​(ฉันไม่สามารถทดสอบวิธีแก้ปัญหาส่วนใหญ่ได้ดังนั้นฉันจะเชื่อใจคุณที่นี่) ข้อความปฏิเสธความรับผิดชอบ:ฉันรู้ว่ามีอัลกอริทึมที่รวดเร็วอยู่ (ค้นหา "ปัญหาการแยกเส้นทาง") แต่ฉันไม่ได้ดำเนินการด้วยตนเอง

กรณีทดสอบเพิ่มเติม

Before
......
.O..O.
..OO..
......
After
......
..O...
...OO.
..O...
Possible output
......
.>..v.
..vO..
......

Before
.......
.OOOOO.
.O..OO.
.OO..O.
.OOOOO.
.......
After
.......
..OOOOO
.O...O.
.O...O.
.OOOOOO
....O..
Possible output
.......
.>>>>>.
.O..>v.
.Ov..v.
.O>>v>.
.......

Before
..........
.OOO..OOO.
.OOOOOOOO.
.OOO..OOO.
..........
After
..O.......
.OOO..O.O.
..OOOOOOOO
.O.O..OOO.
.......O..
Possible output
..........
.>^O..O>v.
.^O>>>vO>.
.O>^..>vO.
..........

Before
............
.OO..OOOOOO.
.OO......OO.
...OOOOOO...
.O.OOOOOO.O.
...OOOOOO...
.OOOOOOOOOO.
............
After
..........O.
.OO..OOOOO..
.O...O...O..
.O.OOOOOOO..
.O.OOOOOO..O
...OO..OO...
....OOOOOOOO
.OOO........
Possible output
............
.OO..v<<<<^.
.v<......^<.
...OOO>>>...
.O.OOO^OO.>.
...OOv^OO...
.vvvO>>>>>>.
............

Before
................
.OOOOOO.OOOOOOO.
..OO..OOOOOOOOO.
.OOO..OOOO..OOO.
..OOOOOOOO..OOO.
.OOOOOOOOOOOOOO.
................
After
................
..OOOOO.OOOOOOOO
..OO..OOOOOOOOO.
..OO..OOOO..OOOO
..OOOOOOOO..OOO.
..OOOOOOOOOOOOOO
................
Possible output
................
.>>>>>v.>>>>>>>.
..OO..>>^>>>>>v.
.>>v..OOO^..OO>.
..O>>>>>>^..OOO.
.>>>>>>>>>>>>>>.
................

Before
..............................
.OOO.O.O.....O.....O.O.O..O...
..OOO.O...O..OO..O..O.O.......
.....O......O..O.....O....O...
.O.OOOOO......O...O..O....O...
.OO..O..OO.O..OO..O..O....O...
..O.O.O......OO.OO..O..OO.....
..O....O..O.OO...OOO.OOO...O..
.....O..OO......O..O...OO.OO..
........O..O........OO.O.O....
..O.....OO.....OO.OO.......O..
.O.....O.O..OO.OO....O......O.
..O..OOOO..O....OO..........O.
.O..O...O.O....O..O....O...OO.
....O...OO..O.......O.O..OO...
........O.O....O.O....O.......
.OO.......O.OO..O.......O..O..
....O....O.O.O...OOO..O.O.OO..
.OO..OO...O.O.O.O.O...OO...O..
..............................
After
..............................
.OOOOO.......OO.....O..O......
...OO..O...O...O....OO....O...
....O.O......O..OO...OO...O...
.OO.OOOO......OO..O..O........
O.O.OO..O..O..O..OO...O...OO..
.OO.....O....OO.O..O.OO.O.....
......O.....O.....OOO.OO...O..
....O..OOOO..O..O..O.O.O.OO...
..O......O.O........O...O.O...
.O.....OOO.....OO.OO...O...O..
.......OOO..O.O.O...........O.
.O...O.....O...OOOO..O.O....O.
.O..O.O..O.....O......O....OO.
....O..O..O.O......O.....O....
........OOO....O......O..O....
.OO......O..OO..OOO.....O..O..
..O.O....OO..O...OO...O...OO..
.O..OO....O..O...O.O.O.OO.....
..............O............O..
Possible output
..............................
.OOO.O.v.....>.....>.v.O..v...
..>>^.v...>..^>..v..O.v.......
.....<......>..>.....O....O...
.O.<O><O......O...O..O....v...
.<O..O..v<.O..O^..O..>....>...
..<.^.v......OO.O^..>..<O.....
..^....v..v.Ov...>>^.<OO...O..
.....<..OO......O..O...Ov.v<..
........>..O........O^.v.^....
..^.....Ov.....OO.OO.......O..
.^.....^.^..O>.vO....v......O.
..<..Ov^^..O....><..........O.
.O..O...>.v....O..^....^...OO.
....O...<v..O.......<.^..v<...
........O.O....O.v....O.......
.OO.......<.Ov..O.......O..O..
....O....O.<.^...O^v..O.v.OO..
.O^..<<...O.>.v.>.^...<O...v..
..............................

เพื่อให้แน่ใจว่าเชื้อโรคสามารถเคลื่อนที่โดยเซลล์หนึ่งหรือศูนย์เท่านั้นใช่ไหม
Domino

@JacqueGoupil ใช่ถูกต้องแล้ว แต่ละ>^<vสอดคล้องกับการเคลื่อนไหวของขั้นตอนเดียวในทิศทางที่เกี่ยวข้อง
Zgarb

ฉันยังไม่ได้ลองแก้ไข แต่นี่เป็นเครื่องมือในการสร้างกรณีทดสอบเพิ่มเติม :) jsfiddle.net/xd2xns64/embedded/result
Domino

โอ้ระวังมีโอกาสที่สคริปต์จะวนซ้ำไปเรื่อย ๆ หากพยายามเคลื่อนย้ายเซลล์ทั้งหมดจากขอบ แต่จากนั้นเซลล์ขอบจะไม่ไปไหน
Domino

คำตอบ:


3

Octave, 494 496 bytes - 372 byte bonus = 124 ไบต์

function o=G(b,a)
y='.O<^v>';s=(b>46)+0;t=a>46;v=t;f=s;t(:,2:end,2)=t(:,1:end-1);t(2:end,:,3)=t(1:end-1,:,1);t(1:end-1,:,4)=t(2:end,:,1);t(:,1:end-1,5)=t(:,2:end,1);t=reshape(t,[],5);m=size(s,1);p=[0 -m -1 1 m];
function z(n)
f(n+p(s(n)))--;q=find(t(n,:));w=n+p(q);d=min(f(w));q=q(f(w)==d);j=randi(numel(q));s(n)=q(j);f(n+p(q(j)))++;end
for g=find(s)' z(g);end
while any((f~=v)(:)) L=find(s);k=zeros(size(s));for h=L' k(h)=f(h+p(s(h)));end;c=find(k>1);g=c(randi(numel(c)));z(g);end
o = y(s+1);end

ยังคงมีสนามกอล์ฟจำนวนมากที่ต้องทำในคำตอบนี้ แต่ฉันต้องการได้คำอธิบายที่ไม่ดี

ฉันเห็นว่านี่เป็นปัญหาความพึงพอใจของข้อ จำกัด ดังนั้นฉันจึงไปกับการค้นหาแบบฮิวริสติกในท้องถิ่นที่ฉันชอบความขัดแย้งขั้นต่ำ แนวคิดคือให้ตำแหน่งเริ่มต้นกับแต่ละเชื้อโรคในปลายทางที่สามารถเข้าถึงได้เลือกเชื้อโรคสุ่มที่ใช้เซลล์ปลายทางเดียวกับเชื้อโรคอื่น ๆ อย่างน้อยหนึ่งตัวและย้ายไปยังเซลล์ที่ถูกต้องซึ่งมีเชื้อโรคอื่นน้อยอยู่แล้ว ทำซ้ำตามความจำเป็นจนกระทั่งตำแหน่งตรงกับเป้าหมาย

ที่น่าสนใจอัลกอริทึมนี้ไม่ได้รับประกันว่าจะยุติ (ถ้าเป้าหมายไม่สามารถเข้าถึงได้มันจะดำเนินต่อไปเรื่อย ๆ ) แต่ถ้ามันจบลงมันจะรับประกันว่าจะสร้างทางออกที่ถูกต้อง

นี่คือรหัส:

function output = germs(before, after)

%before = ['......';'.O..O.';'.OO.O.';'......'];
%after = ['......';'....O.';'.OO.O.';'..O...'];

symbs = '.O<^v>';
start = (before > 46) + 0;                   %should be called current_board
target = after > 46;                         %destinations on current cell == O
goal = target;
conflicts = start;
target(:, 2:end,2) = target(:, 1:end-1);     %destinations on cell to left
target(2:end, :,3) = target(1:end-1, :,1);   %destinations on cell above
target(1:end-1, :,4) = target(2:end, :,1);   %destinations on cell below
target(:, 1:end-1,5) = target(:, 2:end,1);   %destinations on cell to right
target=reshape(target,[],5);
m = size(start,1);                           %number of rows = offset to previous/next column
offsets = [0 -m -1 1 m];                     %offsets of neighbors from current index


function moveGerm(n)
   conflicts(n+offsets(start(n)))--;         %take germ off board
   move = find(target(n, :));                %get valid moves for this germ
   neighbors = n + offsets(move);            %valid neighbors = current position + offsets
   minVal = min(conflicts(neighbors));       %minimum number of conflicts for valid moves
   move = move(conflicts(neighbors)==minVal);
   mi = randi(numel(move));                  %choose a random move with minimum conflicts
   start(n) = move(mi);                      %add move type to board
   conflicts(n + offsets(move(mi)))++;       %add a conflict on the cell we move to
end

% Generate an initial placement
for g = find(start)'
   moveGerm(g);                              %make sure all germs are moved to valid cells
end

% Repeat until board matches goal
while any((conflicts ~= goal)(:))
   germList = find(start);                   %list of all our germs
   cost = zeros(size(start));                %calculate conflicts for each germ
   for h = germList'
      cost(h) = conflicts(h + offsets(start(h)));
   end
   conflicted = find(cost > 1);              %find those germs that occupy the same cell as another
   g = conflicted(randi(numel(conflicted))); %choose a random germ to move
   moveGerm(g);
end

output = symbs(start+1);                     %use moves as indices into symbol array for output

end

เอาต์พุตสำหรับกรณีทดสอบล่าสุด:

>> gtest
ans =

..............................
.OO>.O.v.....>.....>.v.O..v...
..>^O.v...>..^>..v..O.v.......
.....v......>..>.....O....O...
.O.<^<OO......>...O..O....v...
.<O..O..v<.O..^<..O..>....>...
..<.^.v......OO.O^..<..<O.....
..^....v..v.Ov...>>>.^OO...O..
.....<..OO......O..O...Ov.<<..
........>..O........O^.v.>....
..^.....OO.....OO.OO.......O..
.^.....^.O..O>.vO....v......O.
..<..Ov^^..O....OO..........O.
.O..O...>.v....O..^....^...OO.
....O...<v..O.......<.^..v<...
........O.O....O.v....O.......
.OO.......<.OO..O.......O..O..
....O....O.<.O...O^<..O.v.OO..
.O^..<<...O.>.v.>.>...<O...v..
..............................

Elapsed time is 0.681691 seconds.

เวลาที่ผ่านไปโดยเฉลี่ยน้อยกว่า9 วินาที 1 วินาที * สำหรับ Core i5 อายุ 5 ปีซึ่งมีคุณสมบัติตามที่กำหนด

ฉันกำลังพยายามทำให้ ideone นี้ทำงาน แต่ฉันมีสิ่งที่ฉันเชื่อว่าเป็นปัญหาในการกำหนดขอบเขตด้วยวิธีการจัดการกับฟังก์ชันที่ซ้อนกัน (นี่คือลิงค์ ideone ที่ใช้งานไม่ได้สำหรับการอ้างอิง: http://ideone.com/mQSwgZ )
โค้ดบนideoneกำลังทำงาน ฉันต้องบังคับตัวแปรทั้งหมดให้เป็นโกลบอลซึ่งไม่จำเป็นต้องเรียกใช้งานแบบโลคัล

* ฉันมีบันทึกย่อในเวอร์ชันที่ไม่อัปโหลดของฉันว่าหนึ่งในขั้นตอนนั้นไม่มีประสิทธิภาพดังนั้นฉันจึงลองดูว่าฉันสามารถเพิ่มความเร็วในการประมวลผลได้หรือไม่และเพิ่มอีก 2 ไบต์เวลาดำเนินการลดลงเหลือไม่ถึงวินาที โค้ดและตัวอย่างผลลัพธ์ได้รับการปรับปรุงและอินพุทบน ideone ถูกเปลี่ยนเป็นกรณีทดสอบครั้งสุดท้าย


3

Python 1171 ไบต์ - โบนัส 878.25 ไบต์ = 292.75 ไบต์

from itertools import *;from random import *;R=range;L=len;O=choice;G='O'
def A(a,b):a.append(b)
def D(y,z):
 a=[];b=[];c=[]
 for i in R(L(y)):
  A(c,[])
  for j in R(L(y[0])):
   k=[(i,j),y[i][j]==G,z[i][j]==G,[],0];A(c[i],k)
   for l,m in [(0,1),(1,0)]:
    try:
     n=c[i-l][j-m]
     if k[2]&n[1]:A(n[3],k)
     if k[1]&n[2]:A(k[3],n)
    except:pass
   if k[1]&~k[2]:A(a,k)
   elif k[2]&~k[1]:A(b,k)
 d={}
 for i in a:
  j=[[i]]
  while j:
   k=j.pop();l=[e[0] for e in k]
   while True:
    m=k[-1];n=[o for o in m[3] if o[0] not in l]
    if not n:
     if m in b:A(d.setdefault(i[0],[]),k)
     break
    for o in n[1:]:p=k[:];A(p,o);A(j,p)
    A(k,n[0]);A(l,n[0][0])
 e={}
 for i in a:e[i[0]]=O(d[i[0]])
 def E():return sum(any(k in j for k in i) for i,j in combinations(e.values(),2))
 f=E()
 for i in count():
  t=3**-i/L(a);j=O(a);k=e[j[0]];e[j[0]]=O(d[j[0]]);l=E()
  if not l:break
  else:
   if l>f and random()>t:e[j[0]]=k
   else:f=l
 for i in e.values():
  for j in R(L(i)-1):i[j][4]=i[j+1]
 for i in c:
  for j in R(L(i)):
   k=i[j]
   if 1&~k[1]:i[j]='.'
   elif not k[4]:i[j]=G
   else:l,m=k[0];n,o=k[4][0];i[j]='v>^<'[abs((l-n+1)+2*(m-o))]
 return c

ลิงค์ Ideone: http://ideone.com/0Ylmwq

ใช้เวลาใดก็ได้ตั้งแต่ 1 - 8 วินาทีในกรณีทดสอบล่าสุดโดยเฉลี่ยโดยมีคุณสมบัติสำหรับโบนัส

นี่คือการส่งรหัสกอล์ฟครั้งแรกของฉันดังนั้นจึงอาจไม่ใช่โปรแกรมที่ดีที่สุดสำหรับนักกอล์ฟ อย่างไรก็ตามมันเป็นความท้าทายที่น่าสนใจและฉันก็ชอบมันมาก @Beaker สมควรได้รับการกล่าวถึงเพราะทำให้ฉันนึกถึงว่าการค้นหาโดยอาศัยการแก้ปัญหาเป็นเรื่องสำคัญ ก่อนที่เขาจะโพสต์วิธีแก้ปัญหาของเขาและเป็นแรงบันดาลใจให้ฉันทำซ้ำการค้นหาแรงเดรัจฉานของฉันคือ waaaay นานเกินไปที่จะมีสิทธิ์ได้รับโบนัสในกรณีทดสอบครั้งสุดท้าย (ตามลำดับที่ 69! ซ้ำซึ่งเป็นเลข 99 .)

ฉันไม่ต้องการคัดลอกโซลูชันของ Beaker แบบตรงๆดังนั้นฉันจึงตัดสินใจใช้การจำลองแบบการอบสำหรับการค้นหาแบบฮิวริสติก ดูเหมือนว่าจะช้ากว่าความขัดแย้งขั้นต่ำสำหรับปัญหานี้ (น่าจะเป็นเพราะเป็นอัลกอริธึมการเพิ่มประสิทธิภาพแทนที่จะเป็นข้อ จำกัด ด้านความพึงพอใจ) แต่ก็ยังทำได้ดีภายใน 10 นาที นอกจากนี้ยังมีข้อดีของการเป็นรหัสที่ค่อนข้างเล็ก ฉันใช้เวลามากขึ้นในการเปลี่ยนปัญหามากกว่าที่ฉันทำเพื่อหาวิธีแก้ปัญหา

คำอธิบาย

วิธีการแก้ปัญหาของฉันอาจไม่มีประสิทธิภาพพอสมควร แต่ฉันมีปัญหาเกี่ยวกับการคิดวิธีแก้ปัญหาตามที่เป็นอยู่ดังนั้นฉันจึงต้องเปลี่ยนมันเป็นปัญหาอื่นที่ง่ายต่อการเข้าใจ ฉันรู้ว่ามีสี่สิ่งที่เป็นไปได้สำหรับแต่ละเซลล์ในตาราง:

  • มันไม่มีเชื้อโรคก่อนหรือหลังซึ่งหมายความว่าเราสามารถเพิกเฉยได้
  • มันมีเชื้อโรคมาก่อน แต่ไม่ใช่หลังจากนั้นซึ่งหมายความว่าเราต้องหาทางทำมัน
  • มันไม่เคยมีเชื้อโรคมาก่อน แต่หลังจากนั้นหนึ่งครั้งซึ่งก็หมายความว่าเราต้องหาทางทำมันด้วย
  • มันมีเชื้อโรคทั้งก่อนและหลังซึ่งหมายความว่าเราอาจจะต้องหาทางย้ายมัน แต่ก็อาจจะไม่ได้อีกครั้ง

หลังจากแยกย่อยข้อมูลเป็นคลาสเหล่านั้นฉันก็สามารถที่จะแปลงปัญหาเพิ่มเติม เห็นได้ชัดว่าฉันต้องหาวิธีที่จะส่งเชื้อโรคจาก "ก่อน แต่ไม่ใช่หลังจาก" ตั้งให้เซลล์ในชุด "หลังจาก แต่ไม่ก่อน" ยิ่งไปกว่านั้นเชื้อโรคสามารถเคลื่อนที่เพียงช่องเดียวดังนั้นวิธีเดียวที่พวกมันจะมีผลต่อเซลล์ที่อยู่ไกลออกไปคือการ "ผลัก" เส้นทางที่ไม่แตกของเชื้อโรคเข้าไปในเซลล์นั้น นั่นหมายความว่าปัญหาจะเกิดขึ้นเมื่อพบเส้นทาง X จุดยอด - แยกจากกันบนกราฟซึ่งแต่ละเซลล์ที่มีจมูกเป็นจุดยอดในกราฟดังกล่าวและขอบเป็นตัวแทนเซลล์ที่อยู่ติดกัน

ฉันแก้ปัญหานั้นโดยการสร้างกราฟที่อธิบายไว้ข้างต้น ฉันแจกแจงทุกเส้นทางที่เป็นไปได้จากแต่ละเซลล์ในก่อนและแต่ละเซลล์ในหลังจากนั้นสุ่มสุ่มแต่ละเซลล์ในก่อนหนึ่งเส้นทางที่เป็นไปได้ ในที่สุดฉันใช้การจำลองการอบอ่อนแบบกึ่งสุ่มกลายพันธุ์วิธีการแก้ปัญหาที่อาจเกิดขึ้นจนกว่าฉันจะหาทางออกที่ไม่มีความขัดแย้งสำหรับเส้นทางใด ๆ

รุ่นข้อเขียน

from itertools import *;from random import *;

# redefine some built-in functions to be shorter
R=range;L=len;O=choice;G='O'
def A(a,b):a.append(b)

# The function itself.  Input is in the form of two 2d arrays of characters, one each for before and after.
def D(y,z):
 # Declare the Before-but-not-after set, the After-but-not-before set, and a temp cell array
 # (the cells are temporarily stored in a 2d array because I need to be able to locate neighbors)
 a=[];b=[];c=[]

 # Build the graph
 for i in R(L(y)):
  # Append a row to the 2d temp array
  A(c,[])

  for j in R(L(y[0])):
   # Define the interesting information about the cell, then add it to the temp array
   # The cell looks like this: [position, does it have a germ before?, does it have a germ after?, list of neighbors with germs, final move]
   k=[(i,j),y[i][j]==G,z[i][j]==G,[],0];A(c[i],k)
   for l,m in [(0,1),(1,0)]:
    # Fill up the neighbors by checking the above and left cell, then mutually assigning edges
    try:
     n=c[i-l][j-m]
     if k[2]&n[1]:A(n[3],k)
     if k[1]&n[2]:A(k[3],n)
    except:pass

   # Decide if it belongs in the Before or After set
   if k[1]&~k[2]:A(a,k)
   elif k[2]&~k[1]:A(b,k)

 # For each cell in the before set, define ALL possible paths from it (this is a big number of paths if the grid is dense with germs)
 # This uses a bastard form of depth-first search where different paths can cross each other, but no path will cross itself
 d={}
 for i in a:
  j=[[i]]  # Define the initial stack of incomplete paths as the starting node.
  while j:
   # While the stack is not empty, pop an incomplete path of the stack and finish it
   k=j.pop();l=[e[0] for e in k]
   while True:
    # Set the list of next possible moves to the neighbors of the current cell,
    # ignoring any that are already in the current path.
    m=k[-1];n=[o for o in m[3] if o[0] not in l]

    # If there are no more moves, save the path if it ends in an After cell and break the loop
    if not n:
     if m in b:A(d.setdefault(i[0],[]),k)
     break

    # Otherwise, set the next move in this path to be the first move,
    # then split off new paths and add them to the stack for every other move
    for o in n[1:]:p=k[:];A(p,o);A(j,p)
    A(k,n[0]);A(l,n[0][0])

 # Perform simulated annealing to calculate the solution
 e={}
 for i in a:e[i[0]]=O(d[i[0]])  # Randomly assign paths for the first potential solution

 # Define a function for calculating the number of conflicts between all paths, then do the initial calculation for the initial potential solution
 def E():return sum(any(k in j for k in i) for i,j in combinations(e.values(),2))
 f=E()

 # Do the annealing
 for i in count():
  # The "temperature" for simulated annealing is calculated as 3^-i/len(Before set).
  # 3 was chosen as an integer approximation of e, and the function e^(-i/len) itself was chosen because
  # it exponentially decays, and does so slower for larger problem sets
  t=3**-i/L(a)

  j=O(a)              # Pick a random Before cell to change
  k=e[j[0]]           # Save it's current path
  e[j[0]]=O(d[j[0]])  # Replace the current path with a new one, randomly chosen
  l=E()               # Recalculate the number of conflicts

  if not l:break  # If there are no conflicts, we have a valid solution and can terminate
  else:           # Otherwise check the temperature to see if we keep the new move
   if l>f and random()>t:e[j[0]]=k  # Always keep the move if it's better, and undo it with probability 1 - T if it's worse
   else:f=l                         # If we don't undo, remember the new conflict count

 # Set each of the cells' final moves based on the paths
 for i in e.values():
  for j in R(L(i)-1):i[j][4]=i[j+1]

 # Build the output in the form of a 2d array of characters
 # Reuse the temp 2d array from step since its the right size
 for i in c:
  for j in R(L(i)):
   k=i[j]
   # Cells that are empty in the before array are always empty in the output
   if 1&~k[1]:i[j]='.'
   # Cells that aren't empty and don't have a move are always germs in the output
   elif not k[4]:i[j]=G
   # Otherwise draw the move
   else:l,m=k[0];n,o=k[4][0];i[j]='v>^<'[abs((l-n+1)+2*(m-o))]
 return c
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.