我怎样才能更有效地编写这个元胞自动机项目?

How can I code this Cellular Automata project more efficiently?

我是编程新手,我对元胞自动机很感兴趣,所以我决定尝试使用 JavaScript 编写一个脚本来进行编码练习,这样我就可以制作一个个性化的元胞自动机。我创建的元胞自动机项目是针对一个简单的二进制(黑白)2D table CA,它查看一个单元格的 8 个最近邻居的颜色和单元格本身的颜色,并根据'ruleset' table 下面的 CA table 中给出的规则。唯一的问题是我编写的代码需要永远处理每次迭代,显然是因为它需要所有大循环。事实上,当我写这篇文章时,我意识到当 if 语句找到正确的配置时,我可以通过停止当前相邻颜色配置和所有可能配置的集合之间的比较搜索来减少所需的处理能力,但这可能不会减少我想要的数量所需的处理能力,我相信有更多的方法可以让它更快。如果有人能给我一些关于如何进一步降低处理能力的建议,我将非常感激。另外,请用通俗易懂的语言解释您的答案。谢谢!这是代码:

<!DOCTYPE html>
<html>
<head>
<title></title>


<style>
table {border-collapse: collapse;}
table, td, th {border: 1px solid black;}
td {width:1px; height:1px;}
</style>

</head>
<body>

<button onclick="Toggle()">Toggle</button>  

<!-- Toggle runs the Iterate function with a setInterval -->

<button onclick="Iterate()">Iterate</button>
<br>


<script>


document.write("<table>")
for (row=0; row<100; row++) {
document.write("<tr>")
for (col=0; col<100; col++) 
{document.write("<td id = 'R" + row + "C" + col + "'  style='background-color: white' ondblclick='MouseDown(this)' onmousedown='MouseDown(this)' onmouseover='MouseUp(this)'>" + "</td>")}
document.write("</tr>")}
document.write("</table>")

// This is the cellular automata table

document.write("<br>")

document.write("<table>")
for (row=0; row<16; row++) {
document.write("<tr>")
for (col=0; col<32; col++) 
{document.write("<td id = 'id" + (col+32*row) + "'  style='background-color: white' ondblclick='MouseDown(this)' onmousedown='MouseDown(this)' onmouseover='MouseUp(this)'>" + "</td>")}
document.write("</tr>")}
document.write("</table>")

// This is the 'ruleset' table


let determiner = 0
function MouseDown(cell) {determiner = 1
if (cell.style.backgroundColor == "white") {cell.style.backgroundColor = "black"}
else {cell.style.backgroundColor = "white"}}
window.addEventListener('mouseup', function(event){determiner = 0})
function MouseUp(cell) {if (determiner == 1) {
if (cell.style.backgroundColor == "white") {cell.style.backgroundColor = "black"}
else {cell.style.backgroundColor = "white"}}}

// This section provides the click & drag cell colour changing functions



for (i=0; i<512; i++) {
if (i % 512 < 256){this["j1"] = "white"} else {this["j1"] = "black"}
if (i % 256 < 128){this["j2"] = "white"} else {this["j2"] = "black"}
if (i % 128 < 64){this["j3"] = "white"} else {this["j3"] = "black"}
if (i % 64 < 32){this["j4"] = "white"} else {this["j4"] = "black"}
if (i % 32 < 16){this["j5"] = "white"} else {this["j5"] = "black"}
if (i % 16 < 8){this["j6"] = "white"} else {this["j6"] = "black"}
if (i % 8 < 4){this["j7"] = "white"} else {this["j7"] = "black"}
if (i % 4 < 2){this["j8"] = "white"} else {this["j8"] = "black"}
if (i % 2 < 1){this["j9"] = "white"} else {this["j9"] = "black"}
this["compare"+i] = {unit00: j1,unit01: j2,unit02: j3,unit10: j4,unit11: j5,unit12: j6,unit20: j7,unit21: j8,unit22: j9}
}

// This creates an object for each possible block of 9 cells to compare with the actual blocks of cells around each cell in the Iterate() function


function Iterate() {
this["groupvec"] = []
for (i=0; i<100; i++) {
for (j=0; j<100; j++) {
if (i !== 0 && i !== 99) {rownum = [i-1, i, i+1]}
else if (i == 0) {rownum = [99, 0, 1]}
else if (i == 99) {rownum = [98, 99, 0]}
if (j !== 0 && j !== 99) {colnum = [j-1, j, j+1]}
else if (j == 0) {colnum = [99, 0, 1]}
else if (j == 99) {colnum = [98, 99, 0]}
this["group"+"R"+i+"C"+j] = {}
for (r in rownum) {
for (c in colnum) {
this["group"+"R"+i+"C"+j]['unit'+r.toString()+c.toString()] = document.getElementById("R" + rownum[r] + "C" + colnum[c]).style.backgroundColor
}}
this["groupvec"].push( JSON.stringify(this["group"+"R"+i+"C"+j]) )
}}
for (i=0; i<100; i++) {
for (j=0; j<100; j++) {
for (k=0; k<512; k++) {
if (groupvec[j+(100*i)] == JSON.stringify(window["compare"+k.toString()])) {
document.getElementById("R"+i+"C"+j).style.backgroundColor = document.getElementById("id"+k).style.backgroundColor
}}}}}

// This function finds the colours of the cells in a block of 9 cells around each cell, compares them with the 'compare' objects and then changes their colour to the colour of the 'ruleset' table with the same index as the 'compare' object.

let toggler = null
function Toggle() {
if (toggler == null) {toggler = setInterval(Iterate.bind(null), 1000)}
else {clearInterval(toggler); toggler = null}
}

// This provides an automated run function for the CA

</script>


</body>
</html>

您的代码每次迭代循环 5 210 000 次(100 行 * 100 列 * 3 个单元格 * 3 个单元格 + 100 行 * 100 列 * 512 个规则集单元格)。对于每次迭代来说,这是相当多的工作,并且由于您使用 HTML <table>,您不断地从中读取和写入样式,这加剧了这种情况。

如果您想要更可靠的解决方案,请尝试使用 Canvas 来显示 CA 的状态,并使用 JavaScript 数组来处理状态。您仍然可以使用 table 在开始时设置规则集,然后将其存储在数组中,这样当您检查它时,您正在检查内存中的数据数组。

您的代码可能看起来像这样,它循环了 80 000 次(尽管如果合并您的规则集可能会更多):

const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const width = 100;
const height = 100;
const cellWidth = 4;
const cellHeight = 4;

// Access cells with automaton[row][column]
let automaton = Array(height).fill(Array(width).fill(0)));

// Initialise your automaton somehow
// ...

function iterate() {
    // Create the next automaton state to write updates to
    let nextAutomaton = Array(height).fill(Array(width).fill(0)));
    for (let y = 0; y < height; ++y) {
        for (let x = 0; x < width; ++x) {
            // Get the surrounding 8 cells
            // (n + MAX + 1) % MAX will wrap around
            // (n + MAX - 1) % MAX will wrap around
            const surroundingIndices = [
                { x: x, y: (y + height - 1) % height },                       // above
                { x: (x + width + 1) % width, y: (y + height - 1) % height }, // above right
                { x: (x + width + 1) % width, y: y },                         // right
                { x: (x + width + 1) % width, y: (y + height + 1) % height }, // below right
                { x: x, y: (y + height + 1) % height },                       // below
                { x: (x + width - 1) % width, y: (y + height + 1) % height }, // below left
                { x: (x + width - 1) % width, y: y },                         // left
                { x: (x + width - 1) % width, y: (y + height - 1) % height }  // above left
            ];
            for (int i = 0; i < 8; ++i) {
               const cell = automaton[surroundingIndices.y][surroundingIndices.x];
               // Do something based on the value of this surrounding cell
               // This could depend on your ruleset
               // ...
            }
            // Set this cell's value in the next state
            nextAutomaton[y][x] = 1;
            // Render the cell
            context.fillStyle = 'red';
            context.fillRect(x, y, cellWidth, cellHeight);
        }
    }
    // Overwrite the old automaton state
    automaton = nextAutomaton;
}

要为自动机设置动画,您需要使用 window.requestAnimationFrame, which must call itself recursively with the iterate function, and which you can start or stop with your toggle button (see window.cancelAnimationFrame)。