预警:本节使用java的JFrame为例子,请对该场景不适的同学退场。
JFrame
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(() -> { // 将创建窗口过程放进**创建分发线程**中,这样当窗口越来越大时,可以避免一些问题
JFrame frame = new JFrame("Welcome");
frame.setSize(500, 500);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}
Graphics2D
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import javax.swing.*;
public class AlgoFrame extends JFrame{
private int canvasWidth;
private int canvasHeight;
public AlgoFrame(String title, int canvasWidth, int canvasHeight){
super(title);
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
AlgoCanvas canvas = new AlgoCanvas();
// 视口大小根据画布大小调整
setContentPane(canvas);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
}
public AlgoFrame(String title){
this(title, 1024, 768);
}
public int getCanvasWidth(){return canvasWidth;}
public int getCanvasHeight(){return canvasHeight;}
// data
int[] money;
public void render(int[] money){
this.money = money;
repaint();
}
private class AlgoCanvas extends JPanel{
public AlgoCanvas(){
// 双缓存
super(true);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// 抗锯齿
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.addRenderingHints(hints);
// 具体绘制
AlgoVisHelper.setColor(g2d, AlgoVisHelper.Blue);
int w = canvasWidth / money.length;
for(int i = 0 ; i < money.length ; i ++)
AlgoVisHelper.fillRectangle(g2d,
i*w+1, canvasHeight-money[i], w-1, money[i]);
}
/**
* 返回画布大小(自动调用)
* @return
*/
@Override
public Dimension getPreferredSize(){
return new Dimension(canvasWidth, canvasHeight);
}
}
}
助手类
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.lang.InterruptedException;
public class AlgoVisHelper {
private AlgoVisHelper(){}
public static final Color Red = new Color(0xF44336);
public static final Color Pink = new Color(0xE91E63);
public static final Color Purple = new Color(0x9C27B0);
public static final Color DeepPurple = new Color(0x673AB7);
public static final Color Indigo = new Color(0x3F51B5);
public static final Color Blue = new Color(0x2196F3);
public static final Color LightBlue = new Color(0x03A9F4);
public static final Color Cyan = new Color(0x00BCD4);
public static final Color Teal = new Color(0x009688);
public static final Color Green = new Color(0x4CAF50);
public static final Color LightGreen = new Color(0x8BC34A);
public static final Color Lime = new Color(0xCDDC39);
public static final Color Yellow = new Color(0xFFEB3B);
public static final Color Amber = new Color(0xFFC107);
public static final Color Orange = new Color(0xFF9800);
public static final Color DeepOrange = new Color(0xFF5722);
public static final Color Brown = new Color(0x795548);
public static final Color Grey = new Color(0x9E9E9E);
public static final Color BlueGrey = new Color(0x607D8B);
public static final Color Black = new Color(0x000000);
public static final Color White = new Color(0xFFFFFF);
public static void strokeCircle(Graphics2D g, int x, int y, int r){
Ellipse2D circle = new Ellipse2D.Double(x-r, y-r, 2*r, 2*r);
g.draw(circle);
}
public static void fillCircle(Graphics2D g, int x, int y, int r){
Ellipse2D circle = new Ellipse2D.Double(x-r, y-r, 2*r, 2*r);
g.fill(circle);
}
public static void strokeRectangle(Graphics2D g, int x, int y, int w, int h){
Rectangle2D rectangle = new Rectangle2D.Double(x, y, w, h);
g.draw(rectangle);
}
public static void fillRectangle(Graphics2D g, int x, int y, int w, int h){
Rectangle2D rectangle = new Rectangle2D.Double(x, y, w, h);
g.fill(rectangle);
}
public static void setColor(Graphics2D g, Color color){
g.setColor(color);
}
public static void setStrokeWidth(Graphics2D g, int w){
int strokeWidth = w;
g.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
}
public static void pause(int t) {
try {
Thread.sleep(t);
}
catch (InterruptedException e) {
System.out.println("Error sleeping");
}
}
public static void putImage(Graphics2D g, int x, int y, String imageURL){
ImageIcon icon = new ImageIcon(imageURL);
Image image = icon.getImage();
g.drawImage(image, x, y, null);
}
public static void drawText(Graphics2D g, String text, int centerx, int centery){
if(text == null)
throw new IllegalArgumentException("Text is null in drawText function!");
FontMetrics metrics = g.getFontMetrics();
int w = metrics.stringWidth(text);
int h = metrics.getDescent();
g.drawString(text, centerx - w/2, centery + h);
}
}
线段端点
线段拐点
抗锯齿
双缓存
用户看不到画布被清空的一瞬间,也就不会有闪烁的效果了。
概率模拟问题
气泡随机移动(关键字circle)
- 随机生成圆心坐标
- 如何检测之间的碰撞
随机模拟问题(关键字:Money)
房间内有100人,每人有100块钱,每分钟随机的一个人给另一个人1块钱,在一段时间后,这100个人的财富状况是怎样的?
蒙特卡多算法
蒙特卡洛(摩纳哥赌城)方法是一种统计学的方法;是一种模拟。
蒙特卡洛模拟是二战期间,为解决原子弹研制工作中,裂变物质的中子随机扩散问题,美国数学家冯诺伊曼和乌拉姆等提出的一种统计方法,代号:蒙特卡洛。
通过大量随机样本,去了解一个系统,进而得到所要计算的值。
- 拉斯维加斯算法
- 蒙特卡洛求PI值(关键字:PI)
用蒙特卡洛投点法计算Pi的值;
在一个边长为a的正方形内一均匀概率随机投点,该点落在此正方形的内切圆中的概率即为内切圆与正方形的面积比值,即:Pi * (a / 2)^2 : a^2 = Pi / 4。
- 1.随着投点的次数增加,圆周率Pi计算的准确率在增加;
- 2.但当次数达到一定规模时,准确率精度增加在减缓,其原因是生成的随机数是伪随机的,这也是蒙特卡洛算法达不到祖冲之求圆周率精度的内在原因;
- 3.同时在进行两次的1亿次投点中也可以发现,对于相同的投点数由于实验本身的随机性每次的实验结果是不同的
三门问题(关键字:ThreeGatesExperiment)
三门问题:一个节目中,参赛者会看见三扇关闭的门,其中一扇的后面有一辆汽车。当参赛者选定一扇门,但未开启它的时候,节目主持人会开启剩下两扇门的一扇没有汽车的门。之后,主持人问参赛者要不要换另外一扇门?问题是:换另外一扇门会否增加参赛者赢得汽车的概率?
出自美国的电视游戏节目Let’s Make a Deal。问题的名字来自该节目的主持人蒙提·霍尔(Monty Hall)。
结论:换门的话,中奖率为2/3,不换门的话,中奖率为1/3
public class ThreeGatesExperiment {
private int N;
public ThreeGatesExperiment(int N){
if(N <= 0)
throw new IllegalArgumentException("N must be larger than 0!");
this.N = N;
}
public void run(boolean changeDoor){
int wins = 0;
for(int i = 0 ; i < N ; i ++)
if(play(changeDoor))
wins ++;
System.out.println(changeDoor ? "Change" : "Not Change");
System.out.println("winning rate: " + (double)wins/N);
}
private boolean play(boolean changeDoor){
// Door 0, 1, 2
int prizeDoor = (int)(Math.random() * 3);
int playerChoice = (int)(Math.random() * 3);
if( playerChoice == prizeDoor)
// 不换门中奖
return changeDoor ? false : true;
else
// 换门中奖
return changeDoor ? true : false;
}
public static void main(String[] args) {
int N = 10000000;
ThreeGatesExperiment exp = new ThreeGatesExperiment(N);
exp.run(true);
System.out.println();
exp.run(false);
}
}
原理:
P(0号门中奖)+P(1号门中奖)+P(2号门中奖)=1
P(0号门中奖)=1/3
P(1号门中奖)+P(2号门中奖)=2/3
P(1号门中奖)=0=>P(2号门中奖)=2/3
你一定能中奖吗?(关键字WinningPrize)
在游戏里,有一种宝箱,打开这个宝箱获得传奇武器的概率是20%,现在你打开5个这样的宝箱,获得传奇武器的概率是多少?
抽五次不会100%中奖?
只要中奖率不是100%,抽多少次都不能保证100%中奖。
1-(0.8)45=0.67232
1-(0.8)x>0.95
1 / 0.2 = 5 只是平均值,期望值
排序可视化
选择排序 (关键字selectionsort)
选择排序是交换最少的排序,当交换需要的复杂度极高时,比如排序集装箱
插入排序(关键字insert)
归并排序 (merge)
快速排序(QuickSort )
双路快排(Two ways)
三路快排(Three ways)
随机迷宫
- 实质:图的遍历
- 深度优先遍历
- 广度优先遍历
- 出入口固定
回溯去除没有使用的路线
- 利用非递归深度优先(借助栈)求解迷宫问题
如何回溯?找到解的时候,顺着目标点向前找到整个解的路径
广度优先遍历
- 利用非递归深度优先(借助队列)求解迷宫问题
- 如果迷宫有多个解广度优先遍历求得的是最短解
深度优先遍历和广度优先遍历的关系
深度优先
stack.push(入口)
while(lstack.empty())
curPos=stack.pop()
if(curPos==出口)break
对和curPso相邻的每一个可能的方向
if(newPos可达)
stack.push(newPos)
广度优先
queue.enqueue(入口)
while(queue.size()!=0)
curPos=queue.dequeue()
if(curPos==出口)break
对和curPso相邻的每一个可能的方向
if(newPos可达)
queue.enqueue(newPos)
扫雷
乱序算法(Fisher-Yates-Knuth)
给定一副扑克牌,设计一个洗牌算法?你的洗牌算法得到的结果够不够乱?
以扑克牌为例,54张牌,可以得到54!种最终结果。
一个“好”的洗牌算法可以等概率的获得这54!种结果之一
随机抽取元素和第一个元素(未被交换)交换位置
private void shuffle(int[] arr){
for(inti=d;i<n;i ++){
//从[i,n)区间里随机选择元素
intx=(int)(Math.random()*(n-i))+i;
swap(arr,i,x);
优化
private void shuffle(int[] arr){
for(inti=n-1;i>=0;i--){
//从[e,i+1)区间里随机选择元素
intk=(int)(Math.random()*(i+1));
swap(arr,i, x);
floodfill算法
当我们点击空白的按钮之后,展现什么样的数据呢? 以数字为边界
八方向拓展,然后深度优先遍历,递归,好简单,当然也可以使用深度优先遍历的非递归方式或者广度优先遍历来解决
PS中的魔棒选区就是用的floodfill算法
public void open(int x,int y){
if(!inArea(x,y))
throw new IllegalArgumentException("Out of index in open function!")
if(isMine(x,y))
throw new IllegalArgumentException("Cannot open an mine block in open")
open[x][y]=true;
if(numbers[x][y]>0)
return;
for(inti=x-1;i<=x+1;i++)
for(intj=y-1;j<=y+1;j++)
if(inArea(i,j)& lopen[i][j]& !mines[il[j])
open(i,j);
}
生成迷宫(Maze)
- 只有一个入口,一个出口
- 只有一个解
- 路径是连续的
- 绘制在一个方形画布上
- 墙和路径都占一个单元格
- 抽象:迷宫的本质:树,所以生成迷宫就是生成树。
- 基于图,图的遍历结果就是树。每个节点只访问一次,且没有环
- 深度优先遍历的结果是深度优先树
- 广度优先遍历的结果是广度优先树
- 规律性
- 随机队列
- 数组
- 入队:添加进数组
- 出队:从数组中随机选择一个元素
- 链表(随机性更强)
- 入队:随机入队首或者队尾
- 出队:随机从队首或者队尾挑选元素
Move the box 自动求解
共同学习,写下你的评论
评论加载中...
作者其他优质文章