朱莉娅/细胞自动机:到邻里的有效途径

时间:2019-09-26 10:09:18

标签: arrays julia cellular-automata

我想在Julia中实现一个元胞自动机(CA)。应该包装尺寸,这意味着:最左边单元格的左邻居是最右边单元格,等等。

一个关键问题是:如何让一个细胞的邻居计算出下一代细胞的状态?由于应该包装尺寸并且Julia不允许使用负索引(如Python),所以我有了这个主意:

考虑一维CA,第一代就是一维数组:

0 0 1 0 0

如果我们创建一个二维数组,那么第一行向右移动,第三行向左移动,就像这样:

0 0 0 1 0
0 0 1 0 0
0 1 0 0 0

现在,第一列包含第一个单元格及其邻居等的状态。

我认为可以轻松地将其推广到两个或更多个维度。

第一个问题:您认为这是个好主意,还是走错了路?

编辑:第一个问题的答案为否,第二个问题和代码示例已废弃。

第二个问题:如果基本可行,请查看以下草图:

编辑:其他方法,如BogumiłKamiński所建议的,这是使用mod1()获取邻域索引的一维CA的精简版本。

对于任何单元格:  -所有索引的数组  -所有邻居状态的B数组  -C状态转换为一个整数  -D查找下一个状态

function digits2int(digits, base=10)
   int = 0
   for digit in digits
      int = int * base + digit
   end
   return int
end

gen = [0,0,0,0,0,1,0,0,0,0,0]
rule = [0,1,1,1,1,0,0,0]

function nextgen(gen, rule)
  values = [mod1.(x .+ [-1,0,1], size(gen)) for x in 1:length(gen)] # A
  values = [gen[value] for value in values]                         # B
  values = [digits2int(value, 2) for value in values]               # C
  values = [rule[value+1] for value in values]                      # D
  return values
end

for _ in 1:100
  global gen
  println(gen)
  gen = nextgen(gen, rule)
end

下一步应该是将其扩展到二维,现在将尝试...

2 个答案:

答案 0 :(得分:3)

我通常的做法是使用mod1函数进行包装索引。

在这种方法中,无论阵列a的维数是多少,当您要从位置x移出增量dx时,写mod1(x+dx, size(a, 1))就足够了如果x是数组的第一维。

下面是一个简单的示例,它在2D圆环上随机行走,计算访问给定单元的次数(这里我还使用广播来处理一个表达式中的所有维度):

function randomwalk()
    a = zeros(Int, 8, 8)
    pos = (1,1)
    for _ in 1:10^6
        # Von Neumann neighborhood
        dpos = rand(((1,0), (-1,0), (0,1), (0,-1)))
        pos = mod1.(pos .+ dpos, size(a))
        a[pos...] += 1
    end
    a
end

答案 1 :(得分:2)

通常,如果CA的单元格仅依赖于其旁边的单元格,则只需将最后一个元素添加到前面,将第一个元素添加到后面,然后进行模拟,就可以“包装”向量,然后通过再次移除第一个和最后一个元素来“展开”,以得到与起始数组长度相同的结果长度。对于一维情况:

const lines = 10
const start = ".........#........."
const rules = [90, 30, 14]

rule2poss(rule) = [rule & (1 << (i - 1)) != 0 for i in 1:8]

cells2bools(cells) = [cells[i] == '#' for i in 1:length(cells)]

bools2cells(bset) = prod([bset[i] ? "#" : "." for i in 1:length(bset)])

function transform(bset, ruleposs)
    newbset = map(x->ruleposs[x],
        [bset[i + 1] * 4 + bset[i] * 2 + bset[i - 1] + 1
        for i in 2:length(bset)-1])
    vcat(newbset[end], newbset, newbset[1])
end

const startset = cells2bools(start)

for rul in rules
    println("\nUsing Rule $rul:")
    bset = vcat(startset[end], startset, startset[1]) # wrap ends
    rp = rule2poss(rul)
    for _ in 1:lines
        println(bools2cells(bset[2:end-1]))  # unwrap ends
        bset = transform(bset, rp)
    end
end

只要在任何给定单元格的模拟中仅使用相邻单元格,这都是正确的。

如果将其扩展到2D矩阵,则还将“包装”第一和最后一行以及第一和最后一列,依此类推。