Tupper自我指涉公式
最早是在Matrix67的《思考的乐趣》中知道了Tupper自我指涉公式。当时的感觉仅仅是挺神奇的。后来逛Matrix67的博客时,无意间看见的他关于此公式的博文,发现了这句话:
你甚至可以告诉你的MM,说你发现了一个函数,函数在某个位置的图象正好是某某某我爱你的字样。
一语惊醒梦中人啊,这句话让我大受启发,此公式的作用远不止自指涉那一个用途啊。
尝试
高中时候晚上一到家就不想学习,那天实在没事干了,就想起了这个公式,要不我也算一个n?想了半天,实在没啥好词,那就写“九班加油”好了。
以下是步骤:
一、先要整出那种像素很少的图片:
方法很简单,百度“像素字+在线”即可找到相应工具。
二、将这些字的像素转换成二进制码,顺序就是从上到下,从左到右,先列后行。 (太过久远,当时转换的找不到了)
三、然后把二进制码当成一个二进制数,转换成十进制数。当时是用Python算的,毕竟高中一直拿Python当计算器用。
四、到第三步已经可以算完成了,但我当时还想要一个更漂亮的图象,于是自己又画了一个(微软自带画图,用橡皮画的)。
最终效果如下:
这次效果还不错,但太费时间了,做完之后心力憔悴,几年都不想碰这个公式。
Java实现
懒惰(Laziness)是优秀程序员的三大美德之一。所以当这几天又想到Tupper的公式时,我想不能这样下去了,我要一劳永逸的解决这个问题。嗯,编程实现。
方法跟上面半手工的方法是一样的,这学期在学java,所以就用java写了一个,可以实现字符串向图像/n值得转换,以及n值向图像的转换:
import java.util.*;
import java.math.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.awt.image.*;
import java.awt.*;
import java.io.*;
import javax.imageio.*;
public class Tupper{
public static void main(String[] args) throws Exception {
Scanner in = new Scanner(System.in);
//选择文字转图象(n值),或n值转图象
String st; char ch = ' ';
while(ch != 'A' && ch != 'B'){
System.out.print("(a)文字 -> 图象/n值\n(b)n值 -> 图象\n请输入a或b:");
st = in.nextLine();
st = st.toUpperCase();
ch = st.charAt(0);
}
//输入相应参数
if(ch == 'A'){
System.out.print("输入一个字符串:");
String content = in.nextLine();
System.out.print("输入图片的行数:");
int lines = in.nextInt();
System.out.print("输入图片的名称:");
in.nextLine();
String name = in.nextLine();
name = name + ".png";
BigInteger number = createImage(content, lines, new Font("黑体", Font.BOLD, 72));
System.out.println("n = " + number);
translateImage(lines, 10, number, new File(name));
}
else{
System.out.print("输入行数:");
int lines = in.nextInt();
System.out.print("输入n值:");
BigInteger number = in.nextBigInteger();
System.out.print("输入图片的名称:");
in.nextLine();
String name = in.nextLine();
name = name + ".png";
translateImage(lines, 5, number, new File(name));
}
}
public static void translateImage(int lines, int side, BigInteger number, File outFile) throws Exception {
number = number.divide(BigInteger.valueOf(lines));
//判断所需要的列数
BigInteger x = number;
int k = 0;
while(x.compareTo(BigInteger.valueOf(0)) != 0){
k++;
x = x.divide(BigInteger.valueOf(2));
}
k = k/lines + 1;
//计算图片的长宽
int width = (k + 2) * side;
int height = (lines + 2) * side;
//创建图片
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);//背景色
//绘制图象
g.setColor(Color.black);
for(int rank = k; rank > 0; --rank)
for(int line = 1; line <= lines; ++line){
if(number.mod(BigInteger.valueOf(2)).compareTo(BigInteger.valueOf(1)) == 0)
g.fillRect(rank * side, line * side, side, side);
number = number.divide(BigInteger.valueOf(2));
}
g.dispose();
ImageIO.write(image, "png", outFile);
}
//<根据str,font的样式以及输出文件目录>
public static BigInteger createImage(String str, int lines, Font font) throws Exception{
//<获取font的样式应用在str上的整个矩形>
Rectangle2D r=font.getStringBounds(str, new FontRenderContext(AffineTransform.getScaleInstance(1, 1),false,false));
int unitHeight=(int)Math.floor(r.getHeight());//<获取单个字符的高度>
//<获取整个str用了font样式的宽度这里用四舍五入后+1保证宽度绝对能容纳这个字符串作为图片的宽度>
int width=(int)Math.round(r.getWidth())+1;
int height=unitHeight;//<把单个字符的高度+3保证高度绝对能容纳字符串作为图片的高度>
//<创建图片>
BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);//<先用白色填充整张图片,也就是背景>
g.setColor(Color.black);//<在换成黑色>
g.setFont(font);//<设置画笔字体>
g.drawString(str, 0, font.getSize());//<画出字符串>
int ranks = width / (height / lines);
//生成n值
BigInteger ans = BigInteger.valueOf(0);
BigInteger k = BigInteger.valueOf(1);
for(int rank = ranks-1; rank >= 0; --rank){
for(int line = 0; line < lines; ++line){
//判断一格应该选择黑色还是白色
int x = rank * width / ranks;
int y = line * height / lines;
int black = 0;
int white = 0;
for(int i = x; i < x + width / ranks && i < width; ++i)
for(int j = y; j < y + height / lines && j < height; ++j){
if(image.getRGB(i, j) < -1) black++;
else white++;
}
if(white <= black)
ans = ans.add(k);
k = k.multiply(BigInteger.valueOf(2));
}
}
g.dispose();
return ans.multiply(BigInteger.valueOf(lines));
}
}
注:代码中有“<>”注释的代码来自一个帖子
第一步获取像素字的方法可谓简单粗暴,一个格子了黑的多就全涂黑,白的多就全抹白。createImage()方法的效果其实并不是很好,只能依靠设置更大的行数(lines)来弥补。下面这张图的行数是30,n值已经比自指涉的那个n长出一倍:
translateImage()这个方法的效果不错,生成的原本的自指涉公式:
最后,按照Matrix67教的,我发现了一个函数
lines = 30
n = 1700588472888268386616099055151107898449466865618167050285900960468542510070550603295531262008076593778655726752857952009189287582683506343855996761652624418921380642270818749133295248126528444626652177756015782801108909393833066453966737590118955916237832946837998688814125346884114535839563585674380557033157390711191161262252298646605632005244091016308467189777828038651948144748565265821649377396689499727131791596575029207578753839278182223578330856732779310321233622275648824557927333543050268932249064955918609670005302824643489254418012849456997594230366888919853450399152782431350010426750850595513305806612377128227982913907954345543984500933887768590182246191125121073030646399643723788666062583892353889726370650995025190770300323814544511187508718162523521106939052592139639700356810828408406908408950067508616713653777281938984193281039850266071213490536657412974701161341646115168498734446257226358803692953684933931059272202364422508820447005930030484806216628824934611848543276037919944519596448031976520837068552069481311886982420661403011028827675081062291800881001130125590179590077783746050556387977851206273823345924453346485792316092203264157490951650097510528229247315801862041466935134393189561023114338332186743559193935505120236089076289633627176402439067659646455323772498015308897717981379567143759631859179276049776386054228601049252580878243258003051965187006602458508067200388519473179363843054248002234697297485520223798829546734857559807574944906003497490572107490349592258607775938416702447137191007310422330506295553842736329708500411220438696879908250551501451342420823189042759257807711326709766940381266510710299294416590336659774336421802856185511488693102040421767571457197647012532788760025442876304138356071731864054370031053886185086327881223863046312261747909926443943457076157546429831417483270184775374849786359811656228219685111193045326387567729913087854789686917223172713638744798668060462670749973122791469818840345386092800880045237244587908556715296798447155224952540889516098223823912960