เครื่องจำลองการแพร่กระจายไฟ


28

สมมติว่าเรามีเมทริกซ์ดังนี้:

11111
12221
12321
12221
11111

เมทริกซ์นี้แสดงถึงภูมิประเทศและแต่ละเซลล์แสดงถึงส่วนของภูมิประเทศ จำนวนในแต่ละเซลล์หมายถึงเวลาที่เป็นส่วนหนึ่งของภูมิประเทศที่จะต้องมีการเผาไหม้สมบูรณ์ (นาทีถ้ามีหน่วยวัดเป็นสิ่งจำเป็น) ตามที่มันติดไฟ หากไฟเริ่มต้นที่ตำแหน่งที่กำหนด (เซลล์) เซลล์นั้นจะต้องถูกเผาอย่างสมบูรณ์ก่อนที่ไฟจะแพร่กระจายไปยังเซลล์ที่อยู่ติดกัน (แนวนอนและแนวตั้งเท่านั้นไม่ใช่แนวทแยงมุม) ดังนั้นหากเกิดเพลิงไหม้ขึ้นที่ตำแหน่งกึ่งกลางไฟจำเป็นต้องใช้:

11111        11111        11111        11011        10001        00000
12221  3 m.  12221  2 m.  12021  1 m.  11011  1 m.  00000  1 m.  00000
12321 -----> 12021 -----> 10001 -----> 00000 -----> 00000 -----> 00000
12221        12221        12021        11011        00000        00000
11111        11111        11111        11011        10001        00000

คำอธิบาย:

  • ไฟเริ่มต้นที่ [2,2] (อิง 0) ซึ่งมีระยะเวลาการเผาไหม้เป็น 3
  • หลังจาก 3 นาที [1,2], [2,1], [2,3], [3,2] เริ่มไหม้
  • หลังจาก 2 นาทีเซลล์เหล่านั้นจะสิ้นสุดการเผาไหม้และไฟจะแพร่กระจายไปยังเซลล์ที่อยู่ติดกันทั้งหมด แต่ [0,2], [2,0], [2,4], [0,4] ต้องการเพียง 1 นาทีในการเผาไหม้ดังนั้น
  • หลังจาก 1 นาทีเซลล์เหล่านั้นจะถูกเผาและเซลล์จะแพร่กระจายไปยังเซลล์ที่อยู่ติดกัน
  • หลังจากผ่านไปอีก 1 นาทีเซลล์ที่เหลือจากขั้นตอนที่ 3 การเผาไหม้และไฟจะแพร่กระจายไปยังเซลล์ที่อยู่ติดกัน (ซึ่งถูกเผาแล้วจึงไม่มีอะไรเกิดขึ้น)
  • หลังจาก 1 นาทีที่ผ่านมาไฟก็จะไหม้พื้นที่ทั้งหมด

ดังนั้นวิธีแก้ปัญหาสำหรับกรณีนี้คือ 8 นาที หากไฟเริ่มต้นในเซลล์ซ้ายสุดด้านบน [0,0]:

11111     01111     00111     00011     00001     00000
12221  1  12221  1  02221  1  01221  1  00121  1  00011   1
12321 --> 12321 --> 12321 --> 02321 --> 01321 --> 00321  -->
12221     12221     12221     12221     02221     01221
11111     11111     11111     11111     11111     01111

00000     00000     00000     00000     00000
00000  1  00000  1  00000  1  00000  1  00000
00221 --> 00110 --> 00000 --> 00000 --> 00000
00221     00121     00020     00010     00000
00111     00011     00001     00000     00000

ดังนั้นเวลาทั้งหมดคือ 10 นาที

ความท้าทาย

ให้ NxM matrix (N> 0, M> 0) ของค่าจำนวนเต็มที่แทนเวลาที่ทุกเซลล์ต้องใช้อย่างสมบูรณ์เขียนโปรแกรม / ฟังก์ชันที่สั้นที่สุดที่ใช้ matrix นั้นและจำนวนเต็มคู่กับตำแหน่งที่ไฟเริ่ม และส่งคืน / พิมพ์เวลาที่จำเป็นสำหรับไฟในการใช้พื้นที่ทั้งหมดอย่างสมบูรณ์

  • ทุกเซลล์จะมีเวลาการเผาไหม้เป็นบวก (ไม่เป็นศูนย์) คุณไม่สามารถถือว่าค่าสูงสุดสำหรับเซลล์
  • เมทริกซ์ไม่จำเป็นต้องเป็นสี่เหลี่ยมจัตุรัสหรือสมมาตร
  • เมทริกซ์สามารถเป็นดัชนี 0 หรือ 1 ดัชนีตามที่คุณต้องการ
  • สามารถกำหนดตำแหน่งเป็นพารามิเตอร์เดียวโดยมี tuple ของจำนวนเต็มสองพารามิเตอร์แยกกันของรูปแบบที่เหมาะสมอื่น ๆ
  • ไม่สามารถระบุขนาดของเมทริกซ์เป็นพารามิเตอร์อินพุต
  • คุณไม่จำเป็นต้องส่งออกทุกขั้นตอนขั้นกลางเพียงแค่จำนวนเวลาที่ถาม แต่ฉันจะไม่บ่นว่าขั้นตอนนั้นเป็นภาพใด ๆ

ตัวอย่างอื่น:

Fire starts at [1,1] (a '>' represents a minute):

4253   4253   4253   4153   4043   3033   2023    0001   0000
2213 > 2113 > 2013 > 1003 > 0002 > 0001 > 0000 >> 0000 > 0000 
1211   1211   1211   1111   1001   0000   0000    0000   0000

Output: 9

นี่คือดังนั้นโปรแกรมที่สั้นที่สุดสำหรับแต่ละภาษาจะชนะ!


1
@ LeanderMoesinger ต้องทำงานกับเมทริกซ์ใด ๆ สิ่งที่ฉันหมายถึงคือโปรแกรมหรือฟังก์ชั่นของคุณไม่สามารถยอมรับมิติของเมทริกซ์เป็นพารามิเตอร์อินพุต แต่แน่นอนว่าคุณสามารถคำนวณมิติเหล่านั้นภายในโค้ดของคุณ
Charlie

สามารถนำเข้าเป็นหมายเลขเดียวในการสั่งซื้อคอลัมน์ที่สำคัญ ? นั่นคือรายการเมทริกซ์จะมีหมายเลขลงแล้วข้าม
Luis Mendo

1
@ LuisMendo ใช่แน่นอน แต่โปรดทราบว่าเวลาการเบิร์นของทุกเซลล์สามารถมากกว่า 9 ได้ถ้านั่นสำคัญสำหรับส่วน "หมายเลขเดียว"
Charlie

ขอบคุณ ไม่มันไม่สำคัญ ฉันหมายถึงตัวเลขเดียว แต่อาจมีหลายหลัก จำนวนจะอยู่ในช่วงตั้งแต่1ถึงM*N
Luis Mendo ใน

คำตอบ:


12

Matlab, 235 257 190 182 178 ไบต์

อินพุต: เมทริกซ์ A , 1x2 เวกเตอร์pที่มีพิกัดเริ่มต้น

function t=F(A,p)
[n,m]=size(A);x=2:n*m;x(mod(x,n)==1)=0;B=diag(x,1)+diag(n+1:n*m,n);k=sub2ind([n m],p(1),p(2));t=max(distances(digraph(bsxfun(@times,((B+B')~=0),A(:))'),k))+A(k)

คำอธิบาย:

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

5   7   7   10
5   2   2   10
4   5   2   6

เราได้รับกราฟที่เชื่อมต่อ:

graph

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

เวอร์ชันที่ไม่อัปโหลดและแสดงความคิดเห็นพร้อมค่าเริ่มต้นตัวอย่าง:

% some starting point
p = [3 2];
% some random 5x6 starting map, integers between 1:10
A = randi(10,5,6); 

function t=F(A,p)
% dimensions of A
[n,m] = size(A);
% create adjacency matrix
x=2:n*m;
x(mod(x,n)==1)=0;
B = diag(x,1)+diag(n+1:n*m,n);
B = B+B';
B = bsxfun(@times,(B~=0),A(:))';
% make graph object with it
G = digraph(B);
% starting node
k = sub2ind([n m], p(1), p(2));
% calculate the shortest distance to all nodes from starting point
d = distances(G,k);
% the largest smallest distance will burn down last. Add burntime of initial point
t = max(d)+A(k);

1
ยินดีต้อนรับสู่ PPCG!
Stephen

วิธีการที่ดีมากและอธิบายได้ดีมาก!
Charlie

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

1
@LeanderMoesinger สแปมทำตัวเหมือนพื้นที่เอาท์พุทของโปรแกรมของคุณหรือไม่? ตัวอย่างเช่นหากสแปมเข้าสู่ STDERR หรือเทียบเท่าและผลลัพธ์เข้าสู่ STDOUT หรือเทียบเท่าคุณควรทำการลบทิ้ง ถ้าพวกมันทั้งคู่อยู่ในจุดเดียวกันฉันก็ไม่รู้
Stephen

@ มันเป็นพื้นที่เอาท์พุทที่แตกต่างกัน แต่ฉันสามารถหลีกเลี่ยงมันทั้งหมดโดยการใส่ทุกอย่างในหนึ่งบรรทัด ขอบคุณสำหรับการชี้แจง!
Leander Moesinger

9

JavaScript (ES6), 156 152 146 144 143 ไบต์

บันทึกแล้ว 1 ไบต์ขอบคุณ Kevin Cruijssen

การใช้งานที่ค่อนข้างไร้เดียงสา รับอินพุตในรูปแบบ currying(a)(s)โดยที่aคืออาเรย์สองมิติและsเป็นอาร์เรย์ของจำนวนเต็มสองจำนวน [ x, y ] ที่แสดงถึงพิกัด 0-based ของตำแหน่งเริ่มต้น

a=>s=>(g=t=>(a=a.map((r,y)=>r.map((c,x)=>(z=(h,v)=>(a[y+~~v]||[])[x+h]<1)(-1)|z(1)|z(0,-1)|z(0,1)|x+','+y==s&&c?u=c-1:c),u=-1),~u?g(t+1):t))(0)

จัดรูปแบบและแสดงความคิดเห็น

a => s => (                                // given a and s
  g = t => (                               // g = recursive function with t = time counter
    a = a.map((r, y) =>                    // for each row r of the input array:
      r.map((c, x) =>                      //   for each cell c in this row:
        (                                  //     z = function that takes
          z = (h, v) =>                    //         2 signed offsets h and v and checks
            (a[y + ~~v] || [])[x + h] < 1  //         whether the corresponding cell is 0
        )(-1) | z(1) |                     //     test left/right neighbors
        z(0, -1) | z(0, 1) |               //     test top/bottom neighbors
        x + ',' + y == s                   //     test whether c is the starting cell
        && c ?                             //     if at least one test passes and c != 0:
          u = c - 1                        //       decrement the current cell / update u
        :                                  //     else:
          c                                //       let the current cell unchanged
      ),                                   //   end of r.map()
      u = -1                               //   start with u = -1
    ),                                     // end of a.map() --> assign result to a
    ~u ?                                   // if at least one cell was updated:
      g(t + 1)                             //   increment t and do a recursive call
    :                                      // else:
      t                                    //   stop recursion and return t
  )                                        // end of g() definition
)(0)                                       // initial call to g() with t = 0

กรณีทดสอบ


==0สามารถตีกอล์ฟได้<1หากฉันไม่ผิด
Kevin Cruijssen

1
@KevinCruijssen นี้ปลอดภัยแน่นอนเพราะundefined<1เป็นเท็จ ขอบคุณ!
Arnauld

8

อ็อกเทฟ 67 ไบต์

function n=F(s,a)n=0;do++n;until~(s-=a|=imdilate(~s,~(z=-1:1)|~z')

ลองออนไลน์!

หากต้องการเห็นภาพผลลัพธ์ระดับกลางคุณสามารถทำได้ ลองสิ่งนี้!

ฟังก์ชันที่ใช้เป็นเมทริกซ์อินพุตของภูมิประเทศ aและพิกัดเริ่มต้นเป็นเมทริกซ์ที่ 0 & 1 ที่มีขนาดเท่ากับภูมิประเทศ

อันที่จริงไม่จำเป็นต้องมี endfunctionรันตัวอย่างใน tio มันควรจะเพิ่ม

คำอธิบาย:

ใช้การขยายภาพทางสัณฐานวิทยาซ้ำ ๆ บนภูมิประเทศและลบพื้นที่ที่ถูกเผาออกจากมัน

คำตอบที่ไม่ดี

function n = Fire(terrain,burned)
    n = 0;
    mask = [...
            0  1  0
            1  1  1
            0  1  0];
    while true
        n = n + 1;
        propagation = imdilate(~terrain, mask);
        burned = burned | propagation;
        terrain = terrain - burned;
        if all(terrain(:) == 0)
            break;
        end
    end
end

นี่เป็นคำตอบที่ดี แต่บางทีอัลกอริทึมกำลังนับสถานะเริ่มต้นเป็นขั้นตอนและส่งคืน 11 แทน 10 ในตัวอย่างของคุณ ถ้าฉันเปลี่ยนเซลล์เริ่มต้นเป็น [3 3] ผลลัพธ์คือ 9 แทน 8
Charlie

@CarlosAlejo โอ้ไม่ดีของฉัน คำตอบการปรับปรุงเปลี่ยนแปลงไปn=1 n=0
rahnema1

7

MATL , 26 25 ไบต์

ฉันอยากเห็นคำตอบเพิ่มเติมโดยใช้ภาษา golfiest ที่นี่

`yy)qw(8My~1Y6Z+fhy0>z}@&

รูปแบบอินพุตคือ:

  • อินพุตแรกคือเมทริกซ์ที่ใช้;เป็นตัวคั่นแถว

  • อินพุตที่สองคือตัวเลขเดียวซึ่งระบุรายการของเมทริกซ์ตามลำดับคอลัมน์หลัก 1 รายการ (อนุญาตโดยการท้าทาย) ตัวอย่างเช่นการประสานงานคอลัมน์หลักของแต่ละรายการในเมทริกซ์ 3 × 4 ได้รับจาก

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

    พิกัดดังนั้นสำหรับตัวอย่างเช่น 1-based (2,2) 5สอดคล้องกับ

ลองออนไลน์! หรือตรวจสอบกรณีทดสอบทั้งหมด

คำอธิบาย

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

ดูโปรแกรมที่รันทีละขั้นตอนด้วยรหัสที่แก้ไขเล็กน้อย

รหัสความคิดเห็น:

`      % Do...while
  yy   %   Duplicate top two arrays (matrix and array of positions to be decremented)
       %   In the first iteration this implicitly takes the two inputs
  )    %   Reference indexing. This gives the values that need to be decremented
  q    %   Decrement
  w    %   Swap. This brings the array of positions that have been decremented to top
  (    %   Assignment indexing. This writes the decremented values back into their
       %   positions
  8M   %   Push array of positions again
  y    %   Duplicate decremented matrix
  ~    %   Negate. This replaces zeros by 1, and nonzeros by 0
  1Y6  %   Push predefined literal [0 1 0; 1 0 1; 0 1 0] (4-neighbourhood)
  Z+   %   2D convolution, maintaining size
  f    %   Find: gives column-major indices of neighbours of totally burnt entries
  h    %   Concatenate. This updates the array of positions to be decremented
  y    %   Duplicate decremented matrix
  0>   %   This gives 1 for positive entries, and 0 for the rest
  z    %   Number of nonzeros. This is the loop condition (*)
}      % Finally (execute before exiting loop)
  @    %   Push iteration number. This is the output
  &    %   Specify that the final implicit display function will display only the top
       %   of the stack
       % Implicit end. If the top of the stack (*) is not 0 (i.e. there are entries
       % that have not been totally burnt) the loop proceeds with the next iteration.
       % Else the "finally" branch is executed and the loop is exited
       % Implicit display (only top of the stack)

2
นี่คือสิ่งที่ฉันเรียกว่ารหัสสั้น ๆ ! :-)
Charlie


4

Python 3 , 277 266 ไบต์

def f(m,s):
 p={s};w=len(m);t=0
 while sum(sum(m,[])):
  t+=1;i=0
  for x,y in p:
   try:m[x][y]=max(0,m[x][y]-1)
   except:0
  for v in sum(m,[]):
   if v<1:
    for l in[(1,0),(-1,0),(0,1),(0,-1)]:a,b=max(0,i%w+l[0]),max(0,i//w+l[1]);p.add((a,b))
   i+=1
 print(t)

ลองออนไลน์!

กำหนดฟังก์ชั่นfที่ใช้เมทริกซ์ 2D และอันดับของ tuple สิ่งแรกที่ฟังก์ชั่นไม่สามารถกำหนดชุดของสิ่งอันดับที่มีมูลค่า tuple p={s}เริ่มต้นผ่านใน: จากนั้นฟังก์ชั่นจะผ่านทุก tuple ของจุดในpและลบหนึ่งจากเมทริกซ์mณ จุดนั้นยกเว้นว่าค่านั้นเป็นศูนย์อยู่แล้ว จากนั้นก็วนซ้ำmpอีกครั้งการหาจุดทั้งหมดที่มีค่าเป็นศูนย์และเพิ่มสี่เพื่อนบ้านที่ชี้ไปที่การตั้งค่า นี่คือเหตุผลที่ฉันเลือกใช้ชุดเนื่องจากชุดใน Python ไม่อนุญาตให้มีค่าซ้ำกัน (ซึ่งจะทำให้การลบมากขึ้น) แต่เนื่องจากรายการดัชนีห่อ (เช่นlist[-1] == list[len(list)-1]) ดัชนีจะต้องมีการ จำกัด pเพื่อให้พวกเขาไม่ได้ไปในเชิงลบและเพิ่มพิกัดความผิดที่จะ

ไม่มีอะไรพิเศษยังคงคุ้นเคยกับการเล่นกอล์ฟ ห้องสำหรับการปรับปรุงอย่างแน่นอนที่นี่ฉันจะให้มันแตก


คุณสามารถเขียนตัวอย่างการดำเนินการได้ที่Try it onlineเพื่อให้เราทุกคนสามารถทดสอบโค้ดของคุณได้หรือไม่?
Charlie

@CarlosAlejo แน่นอนเพิ่มไปยังโพสต์
MooseOnTheRocks

4

APL (Dyalog) , 93 66 57 ไบต์

{⍵{^/,0≥⍺:0⋄1+x∇⍵∨{∨/,⍵∧⍲/¨2|⍳3 3}⌺3 30=x←⍺-⍵}(⊂⍺)≡¨⍳⍴⍵}

ลองออนไลน์! หรือเห็นภาพออนไลน์!


ฟังก์ชั่นนี้ใช้เมทริกซ์ภูมิประเทศเป็นอาร์กิวเมนต์ที่ถูกต้องและพิกัด (อิง 1) ของการยิงครั้งแรกเป็นอาร์กิวเมนต์ซ้าย ส่งคืนจำนวนนาทีที่จำเป็นสำหรับการเขียนทุกอย่าง


อัพเดท

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


TIO เพิ่งอัพเกรดเป็น Dyalog 16.0ซึ่งหมายความว่าตอนนี้เรามีผู้ดำเนินการฉลุใหม่


คำตอบที่ดีมาก! เอาท์พุทกลางช่วยให้เห็นภาพความคืบหน้า!
Charlie

2

Python 2 , 268 ไบต์

def f(m,y,x):t,m[y][x]=m[y][x],0;g(m,t)
def g(m,t):
	n,h,w=map(lambda r:r[:],m),len(m),len(m[0])
	for l in range(h*w):r,c=l/h,l%h;n[r][c]-=m[r][c]and not all(m[r][max(c-1,0):min(c+2,w+1)]+[m[max(r-1,0)][c],m[min(r+1,h-1)][c]])
	if sum(sum(m,[])):g(n,t+1)
	else:print t

ลองออนไลน์!

วนซ้ำซ้ำเมื่อเวลาผ่านไปซึ่งจำนวนของไพ่ทั้งหมดจะลดลงหากอยู่ติดกับ cardinally กับอัลกอริธึมที่ตรงไปตรงมามาก 0 ผมเชื่อว่ายังคงสามารถเล่นกอล์ฟเพื่อประสิทธิภาพบูลีน ...

* หมายเหตุ: 'ลองออนไลน์!' ของฉัน รหัสรวมถึงการบันทึกการดีบักโบนัสในส่วนท้าย ฉันชอบที่จะดูความคืบหน้าของอัลกอริทึม


2

Haskell , 138 133 ไบต์

u#g|all((<=0).snd)g=0|2>1=1+(u:[[(x+1,y),(x-1,y),(x,y-1),(x,y+1)]|((x,y),0)<-n]>>=id)#n where n=d<$>g;d p|elem(fst p)u=pred<$>p|2>1=p

ลองออนไลน์!

ถือว่าอินพุตเป็นรายการของ ((x, y), เซลล์) Ungolfed:

type Pos = (Int, Int)

ungolfed :: [Pos] -> [(Pos, Int)] -> Int
ungolfed burning grid
  | all ((<=0).snd) grid = 0 
  | otherwise = 1 + ungolfed (burning ++ newburning) newgrid
 where
  newgrid = map burn grid
  burn (pos,cell) | pos `elem` burning = (pos, cell - 1)
                  | otherwise = (pos, cell)
  newburning = do
    ((x,y),cell) <- newgrid
    guard (cell <= 0)
    [(x+1,y),(x-1,y),(x,y-1),(x,y+1)]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.