在上一篇中是通过缩小,旋转图形实现希尔伯特曲线,会受到整型运算干扰。这次将使用矩阵存储每个点的序号,完全避免整型运算的影响。
希尔伯特曲线矩阵
一阶希尔伯特曲线矩阵
二阶希尔伯特曲线矩阵
将二阶希尔伯特曲线矩阵分为4个象限
能观察到4个象限的与一阶希尔伯特曲线矩阵有较大关系。
比如第3象限为一阶希尔伯特曲线矩阵关于对角线 0,2对称或逆时针旋转90度
第2象限为一阶希尔伯特曲线矩阵加4 (4=前一阶矩阵的元素个数)
第1象限为一阶希尔伯特曲线矩阵加4 后乘以2
第4象限为一阶希尔伯特曲线矩阵加4 后乘以3 并逆时针旋转90 度,再关于水平翻转180度(用左手来比划比划:掌心向内,手指向下,弯曲拇指中指无名指,现在小拇指尖为0,食指指尖为4,活动左手,手指向右,手背向内)
这样就非常简单了,后一阶希尔伯特曲线矩阵可通过前一阶希尔伯特曲线矩阵作旋转,加法运算即可。
代码
public final class HibertPlus {
//key为true时关于右倾对角线对称
//key为false时行列交换
public static int[][] transposition(int[][] array,boolean key){
int len=array.length;
int[][] tran = new int[len][len];
for(int i=0;i<len;i++) {
for(int j=0;j<len;j++) {
if(key) {
// tran[i][j]=array[j][len-1-i];
tran[j][i]=array[len-1-i][len-1-j];
}else {
tran[j][i]=array[i][j];
}
}
}
return tran;
}
public static void show(int[][] array) {
int n=array.length;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.printf("%2d\t", array[i][j]);
}
System.out.println();
} System.out.println();
}
public static int[][] up(int[][] old) {
int len=old.length;
int n=0;
while(1<<n<len)n++;
int value=(int) Math.pow(4, n);
int[][] array = new int[2*len][2*len];
//第3象限
int[][] three=transposition(old,true);
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
array[i+len][j]=three[i][j];
}
}
//第4象限
int[][] four=transposition(old,false);
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
array[i+len][j+len]=four[i][j]+3*value;
}
}
//第1象限
int[][] one=old;
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
array[i][j+len]=one[i][j]+2*value;
}
}
//第1象限
int[][] two=old;
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
array[i][j]=two[i][j]+value;
}
}
return array;
}
public static int[][] get(int n){
int[][] array = new int[][] {{1,2},{0,3}};
while(n>1) {
n--;
array=up(array);
}
return array;
}
}
测试
show(get(1));
show(get(2));
show(get(3));
//show(get(4));
//show(get(5));
输出
1 2
0 3
5 6 9 10
4 7 8 11
3 2 13 12
0 1 14 15
21 22 25 26 37 38 41 42
20 23 24 27 36 39 40 43
19 18 29 28 35 34 45 44
16 17 30 31 32 33 46 47
15 12 11 10 53 52 51 48
14 13 8 9 5 4 55 50 49
1 2 7 6 5 7 5 6 61 62
0 3 4 5 5 8 5 9 60 63
希尔伯特曲线动画代码
通过每隔一段时间添加一条直线达到动画效果。
要添加一条直线就得记录上一条直线的末端,如果是第一条直线就记录下来,用作下一条直线首端
如果画完了就关闭service。
public class Test extends Application {
ScheduledService<Number> service;//任务
int[][] array;//希尔伯特曲线矩阵
int value=0;//序列值
int rank=6;//阶数
final int SIZE=600;
AnchorPane anchorPane=new AnchorPane();
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage primaryStage) throws Exception {
// TODO Auto-generated method stub
initArray();
// anchorPane.getChildren().add(view);
initService();
Scene scene=new Scene(anchorPane);
anchorPane.setTranslateX(25);
anchorPane.setTranslateY(25);
primaryStage.setScene(scene);
primaryStage.setWidth(SIZE+50);
primaryStage.setHeight(SIZE+50);
primaryStage.show();
}
public void initArray() {
array = HibertPlus.get(rank);
int len=array.length;
for (int i = 0; i < len/2; i++) {
for (int j = 0; j < len; j++) {
int tmp=array[i][j];
array[i][j]=array[len-1-i][j];
array[len-1-i][j]=tmp;
}
}
}
private int oldi=0,oldj=0;
public void line() {
double length=SIZE/Math.pow(2, rank);
int len=array.length;
System.out.println(" +value "+value);
//可改进为向四个方向搜索目标值
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
if(array[i][j]==value) {
if(oldi<-1) {
oldi=i;
oldj=j;
}else {
Line line =new Line(oldi*length,oldj*length,i*length,j*length);
line.setStroke(Color.RED);
anchorPane.getChildren().add(line);
oldi=i;
oldj=j;
}
}
}
}
value+=1;
if(value>Math.pow(4, rank)) {
service.cancel();
}
}
public void initService() {
service =new ScheduledService<Number>(){
protected Task<Number> createTask() {
Task<Number> task=new Task<Number>(){
protected void updateValue(Number value) {
super.updateValue(value);
line();
}
protected Number call() throws Exception {
return 0;
}
};
return task;
}
};
service.setDelay(Duration.seconds(1));
service.setPeriod(Duration.seconds(0.1));
service.setRestartOnFailure(true);
service.setMaximumFailureCount(4);
service.start();
anchorPane.setOnMouseClicked(e->{
service.cancel();
});
}
}