阿诺德的猫地图


21

挑战

给定具有相同宽度和高度的彩色光栅图像*,输出在Arnold猫图下转换的图像。(*详细信息见下文)

定义

给定图像的大小,N我们假定像素的坐标由0和之间的数字给出N-1

然后定义阿诺德的猫图如下:

坐标[x,y]处的像素移动到[(2*x + y) mod N, (x + y) mod N]

这只是圆环上的线性变换:由于,黄色,紫色和绿色部分被映射回初始正方形mod N

可视化

该地图(我们称之为f)具有以下属性:

  • 它是双射的,意味着可逆:这是矩阵的线性变换[[2,1],[1,1]]。由于它具有行列式1且仅具有整数项,因此逆数也仅具有整数项并由给出[[1,-1],[-1,2]],这意味着它在整数坐标上也是双射的。

  • 它是图像双射图组中的一个扭转元素N x N,这意味着如果将其应用足够多次,您将获得原始图像:f(f(...f(x)...)) = x保证将图应用于自身导致身份的次数减少或等于3*N。在下面的内容中,您可以看到经过多次重复应用Arnold的cat map之后的猫的图像,以及重复应用的外观的动画:

多次重复申请

细节

  • 您的程序不必一定要处理图像,但是2D数组/矩阵,字符串或类似的2D结构也是可以接受的。

  • 您的(0,0)点位于左下角还是左上角都没关系。(或在其他任何角落,如果使用您的语言更方便)。请指定您在提交中使用的约定。

测试用例

矩阵形式([1,2,3,4]是第一行,1具有index (0,0)2具有index (1,0)5具有index (0,1)

 1     2     3     4
 5     6     7     8
 9    10    11    12
13    14    15    16

maps to:

 1    14    11     8
12     5     2    15
 3    16     9     6
10     7     4    13

 --------------------

 1     2     3
 4     5     6
 7     8     9

 map to:

 1     8     6
 9     4     2
 5     3     7

作为图片(左下方是(0,0)):


1
可怜的莉娜。我希望您能保持足够长的迭代时间
Luis Mendo

2
我们可以将图像尺寸作为输入吗?它总是正方形吗?
xnor

1
是的形象始终是方形,我不知道的大小,有什么反对允许吗?
瑕疵

Answers:


10

果冻,9个字节

Zṙ"JC$µ2¡

在线尝试!坐标与答案相同。

说明

      µ2¡   Twice:
Z             Transpose, then
 ṙ"           Rotate rows left by
   JC$          0, -1, -2, -3, …, 1-n units.

这会在一个方向上然后在另一个方向上剪切剪切矩阵。


很棒的算法!
格雷格·马丁

7

MATL,23字节

tt&n:qt&+&y\tb+&y\b*+Q(

(0,0)质询文本中的示例所示,该点位于左上角。

在线尝试!

说明

MATL中的矩阵可以用一个索引而不是两个索引。这称为线性索引,并使用优先顺序。下面的4×4矩阵对此进行了说明,其中每个条目的值与其线性索引一致:

1   5   9  13
2   6  10  14
3   7  11  15
4   8  12  16

在挑战中有两种相似的方法可以实现映射:

  1. 建立一个表示线性索引上的Arnold 逆映射的索引矩阵,并使用它从原始矩阵中选择值。对于4×4的情况,索引矩阵为

     1  8 11 14
    15  2  5 12
     9 16  3  6
     7 10 13  4
    

    告知例如原来5X = 2,ÿ = 1变为X = 3,ý = 2。此操作称为参考索引:使用索引矩阵来告诉您要从原始矩阵中选择哪个元素。这是functon ),它需要两个输入(在其默认配置中)。

  2. 建立代表阿诺德的索引矩阵直接映射线性指标,并用它来值到原来的矩阵。对于4×4的情况,索引矩阵为

     1 10  3 12
     6 15  8 13
    11  4  9  2
    16  5 14  7
    

    告诉我们,新矩阵的条目x = 2,y = 1将被覆盖到具有线性索引的条目上10,即x = 3,y = 2。这称为分配索引:使用索引矩阵,数据矩阵和原始矩阵,然后将数据按指定的索引写入原始矩阵。这是function (,它需要三个输入(在其默认配置中)。

方法1更直接,但方法2却更短。

tt     % Take the input implicitly and push two more copies
&n     % Get its size as two (equal) numbers: N, N
:qt    % Push range [0  1 ... N-1] twice. This represents the original x values
&+     % Matrix of all pairwise additions. This represents x+y
&y     % Push a copy of N onto the top of the stack
\      % Modulo. This is the new y coordinate: y_new
t      % Push another copy
b+     % Bubble up the remaining copy of [0 1 ... N-1] and add. This is 2*x+y
&y     % Push a copy of N onto the top of the stack
\      % Modulo. This is the new x coordinate: x_new
b*+    % Bubble up the remaining copy of N, multiply, add. This computes
       % x_new*N+y_new, which is the linear index for those x_new, y_new 
Q      % Add 1, because MATL uses 1-based indexing
(      % Assigmnent indexing: write the values of the original matrix into
       % (another copy of) the original matrix at the entries given by the
       % indexing matrix. Implicitly display the result

5

Mathematica,44个字节

(n=MapIndexed[RotateLeft[#,1-#2]&,#]&)@*n

Lynn出色的算法的移植。最后一个字符之前是UTF-8编码的不可见的3字节字符U + F3C7 ];Mathematica将其渲染为上标T,并接受矩阵的转置。

Mathematica,54个字节

Table[#2[[Mod[2x-y-1,#]+1,Mod[y-x,#]+1]],{x,#},{y,#}]&

无名函数取两个参数,一个正整数#和2D阵列#2尺寸的#X #,并返回类似的形状的2D阵列。与给定测试用例一样,坐标为{0,0}的点在左上方,x轴为水平。使用[[1,-1],[-1,2]]问题中提到的逆函数直接实现,-1在第一个坐标中使用a 来说明数组在Mathematica中固有地以1索引的事实。如果不允许我们将矩阵的维数作为附加参数,则此解决方案的长度将增加9个字节(用s 代替第一个#-而不是#2-with a=Length@#和所有后续s)。#a


党,打我吧
JungHwan民

3

Python 2,89 82 77 73字节

def f(a):exec'a=[l[-i:]+l[:-i]for i,l in enumerate(zip(*a))];'*2;return a

输入是列表列表
exec内的字符串对列表列表进行转置,并按行索引循环旋转每个列表(从0开始-第3行向右旋转2次)。
此过程对输入进行了2次。

+4个字节将进行N次转换

def f(a,n):exec'a=[l[-i:]+l[:-i]for i,l in enumerate(zip(*a))];'*2*n;return a

2

Haskell,55个字节

m#n|r<-[0..n-1]=[[m!!mod(2*y-x)n!!mod(x-y)n|x<-r]|y<-r]

用法示例:[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]] # 4-> [[1,14,11,8],[12,5,2,15],[3,16,9,6],[10,7,4,13]]

0,0是左上角。这使用逆变换。


1

Python,69个字节

lambda M:eval("[r[-i:]+r[:-i]for i,r in enumerate(zip(*"*2+"M))]))]")

一个改进杆的转和移两次方法M -> [r[-i:]+r[:-i]for i,r in enumerate(zip(*M))]通过创建和评估字符串两次应用操作

[r[-i:]+r[:-i]for i,r in enumerate(zip(*[r[-i:]+r[:-i]for i,r in enumerate(zip(*M))]))]

假设图像是正方形并且可以将其长度作为输入,这会勉强击败直接转换(70字节):

lambda M,n:[[M[(2*j-i)%n][(i-j)%n]for i in range(n)]for j in range(n)]

1

ImageJ宏,29个字节

v=getPixel((x+y)%w,(2*y+x)%h)
  • 莉娜的公开影像
  • 从过程菜单中选择数学/宏...

这不执行f ^(-1)吗?它在假定移动到的坐标处获取像素值。你可能是说v=getPixel((2*y-x)%w,(x-y)%h)
罗宾·科赫 Robin Koch),

@RobinKoch谢谢,2*x+y更改为2*y+x
rahnema1

那既不是我写的,也不是我的意思。您需要对方法进行逆变换。为此,f(x,y) = (2x+y, x+y)用来描述逆变换f^(-1) = (x-y, 2y-x)。(我的其他评论是错误的。)因此您的代码应为v=getPixel((x-y)%w,(2*y-x)%h)
罗宾·科赫

我测试了公式,结果与问题中的莉娜图像相同
rahnema1

@RobinKoch您可以下载ImageJ并测试这两个公式
rahnema1'1

1

爪哇,160

打高尔夫球:

int[][]f(int[][]m){int x=0,y,l=m.length,r[][]=new int[l][];for(;x<l;++x)r[x]=new int[l];for(x=0;x<l;++x)for(y=0;y<l;++y)r[(x+y)%l][(2*x+y)%l]=m[y][x];return r;}

取消高尔夫:

  int[][] f(int[][] m) {
    int x = 0, y, l = m.length, r[][] = new int[l][];
    for (; x < l; ++x) {
      r[x] = new int[l];
    }
    for (x = 0; x < l; ++x) {
      for (y = 0; y < l; ++y) {
        r[(x + y) % l][(2 * x + y) % l] = m[y][x];
      }
    }
    return r;
  }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.