重绘Panel边框线

最近有一个需求,是重绘panel边框的颜色成指定的颜色,参考了网上的一些示例代码和结合实际情况,形成了自己的解决方案,以下是实现过程。

示例代码

边框绘制类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/// <summary>
/// panel边框绘制类
/// </summary>
public static class PanelBorderPainter
{
private static Color _colorPanelBorder = Color.FromArgb(215, 215, 215); //d7d7d7
private static int BorderSize = 1;

/// <summary>
/// 重绘panel的边框颜色
/// <para>Padding 需要设置为1,例如:panel.Padding = new System.Windows.Forms.Padding(1);</para>
/// <para>默认颜色值是d7d7d7</para>
/// <para>可以通过panel.Tag=Color.FromArgb(255, 0, 0)设置颜色为红色</para>
///<para>注意:该Paint事件应置于InitializeComponent()之后实现,重新生成Designer.cs会导致代码丢失。</para>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public static void Paint(object sender, PaintEventArgs e)
{
if (sender is Control control)
{
#region panel颜色信息通过Tag传递

if (control.Tag != null)
{
if (control.Tag is Color color)
{
_colorPanelBorder = color;
}
}

#endregion


ButtonBorderStyle style = ButtonBorderStyle.Solid;
ControlPaint.DrawBorder(e.Graphics, control.ClientRectangle,
_colorPanelBorder, BorderSize, style,
_colorPanelBorder, BorderSize, style,
_colorPanelBorder, BorderSize, style,
_colorPanelBorder, BorderSize, style);
}
}
}

声明两个Panel

容器panelOuter包含panelInner

1
2
private System.Windows.Forms.Panel panelOuter;
private System.Windows.Forms.Panel panelInner;

关键代码

panelOuter

1
2
3
4
5
this.panelOuter.Padding = new System.Windows.Forms.Padding(1); //Padding属性设置为1。
this.panelOuter.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelOuter.Controls.Add(panelInner); //必须

panelInner

1
this.panelInner.Dock = System.Windows.Forms.DockStyle.Fill;

Form1构造函数

1
2
3
4
5
6
public Form1()
{
InitializeComponent();
this.panelOuter.Paint += new PaintEventHandler(PanelBorderPainter.Paint);
}

答疑

为什么需要两个Panel?
答:因为如果注释掉以下代码,水平拖拽时会出现如下的界面效果。

1
this.panelOuter.Controls.Add(panelInner);

错误重绘示例
取消该行代码注释则页面拖拽效果正常。

是否必须需要两个Panel?
答:不是,内部是DataGridView等控件,其Dock=DockStyle.Fill,可以起到同样的效果。

参考资料

用多项式解标准数独或锯齿数独

最近业余时间迷上了数独求解,所以试图用计算机的方式来解答数独。
接下来,我用四宫的数独为例子,示例怎么利用多项式求解数独。

四宫数独

我们以四宫的每个单元格标上序号,如下图所示
四宫数独
因为每行每列每宫都包含1,2,3,4且不重复。
所以第一行满足 x00+x01+x02+x03=10,x00*x01*x02*x03=24。
其余列的关系依次类推,其余宫的关系也是如此。
若数独题目如下图所示
四宫例子
根据坐标和格子的关系可知:
x00=1,x01=4,x14=1,x15=2

通过以下Python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from sympy import *
x00=Symbol('x00')
x01=Symbol('x01')
x02=Symbol('x02')
x03=Symbol('x03')
x04=Symbol('x04')
x05=Symbol('x05')
x06=Symbol('x06')
x07=Symbol('x07')
x08=Symbol('x08')
x09=Symbol('x09')
x10=Symbol('x10')
x11=Symbol('x11')
x12=Symbol('x12')
x13=Symbol('x13')
x14=Symbol('x14')
x15=Symbol('x15')
print(solve([
# 行之和为10
x00+x01+x02+x03-10,
x04+x05+x06+x07-10,
x08+x09+x10+x11-10,
x12+x13+x14+x15-10,

# 行之积为24
x00*x01*x02*x03-24,
x04*x05*x06*x07-24,
x08*x09*x10*x11-24,
x12*x13*x14*x15-24,

#列之和为10
x00+x04+x08+x12-10,
x01+x05+x09+x13-10,
x02+x06+x10+x14-10,
x03+x07+x11+x15-10,

#列之积为24
x00*x04*x08*x12-24,
x01*x05*x09*x13-24,
x02*x06*x10*x14-24,
x03*x07*x11*x15-24,

#宫之和为10
x00+x01+x04+x05-10,
x02+x03+x06+x07-10,
x08+x09+x12+x13-10,
x10+x11+x14+x15-10,

#宫之积为24
x00*x01*x04*x05-24,
x02*x03*x06*x07-24,
x08*x09*x12*x13-24,
x10*x11*x14*x15-24,

# x00=1,x01=4,x14=1,x15=2
x00-1,
x01-4,
x14-1,
x15-2
],[x00,x01,x02,x03,x04,x05,x06,x07,x08,x09,x10,x11,x12,x13,x14,x15]))

我们可以快速得知结果是

1
2
3
4
1,4,2,3,
3,2,4,1,
2,1,3,4,
4,3,1,2

其余标准数独(只有唯一解)的四宫数独也可以通过该种方式进行求解。

九宫数独

既然4宫可以用多项式求解?那么九宫是不是也可以这样呢?
答案是可以,但是又有点不同。
∵4+4+4+9=3+4+6+8=21
∵4*4*4*9=3*4*6*8=576
x1+x2+x3+x4+x5+x6+x7+x8+x9=45x1*x2*x3*x4*x5*x6*x7*x8*x9=362880
存在{1,2,3,4,5,6,7,8,9}和{1,2,4,4,4,5,7,9,9}两组解。
若我们把8当做-1看待,9当做-2看待
则每行每列每宫之变成了x1+x2+x3+x4+x5+x6+x7+x8+x9=1+2+3+4+5+6+7+(-1)+(-2)=25
则每行每列每宫之变成了x1*x2*x3*x4*x5*x6*x7*x8*x9=1*2*3*4*5*6*7*(-1)*(-2)=10080
参考四宫数独中Python代码的实现,可以写出(9行+9列+9宫)*(和表达式1个+积表达式1个)共54个表达式,
若已知提示数为N个,则加上这个N个表达式,则可以借助Python的强大的计算能力实现求解。
再将-1还原成8,-2还原成9,则完成了标准数独的求解。

锯齿数独

众所周知,DLX算法求解数独比回溯法要来得快很多,但是锯齿数独并不适合于用DLX算法(稀疏矩阵精准覆盖算法求解)
因为锯齿数独依旧满足每行每列1到9不重复,只是9个宫中9个来源的坐标有变化了。
若坐标序号如下图所示
数独下标示意

以下图第一宫为例:
锯齿数独示例

1
2
3
4
x00+x01+x02+x03+x09+x10+x18+x27+x28-25=0 //每宫之和为25  
x00*x01*x02*x03*x09*x10*x18*x27*x28-10080=0 //每宫之积为10080
x27-4=0 //x27=4
x03-(-2)=0 //先用-2代替9

其他宫的逻辑表达式依次类推,这样就能快速求解锯齿数独了。

推翻九宫结论

如下图有39个提示数的标准数独
九宫提示数

对应的python代码如下,仅仅实现了使CPU达到了100%的效果,迟迟出不来结果
所以该种方式求解9宫格的数独要么需要有强悍的电脑,要么还是直接使用DanceLink算法直接求解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
from sympy import *
x00= Symbol('x00')
x01= Symbol('x01')
x02= Symbol('x02')
x03= Symbol('x03')
x04= Symbol('x04')
x05= Symbol('x05')
x06= Symbol('x06')
x07= Symbol('x07')
x08= Symbol('x08')
x09= Symbol('x09')
x10= Symbol('x10')
x11= Symbol('x11')
x12= Symbol('x12')
x13= Symbol('x13')
x14= Symbol('x14')
x15= Symbol('x15')
x16= Symbol('x16')
x17= Symbol('x17')
x18= Symbol('x18')
x19= Symbol('x19')
x20= Symbol('x20')
x21= Symbol('x21')
x22= Symbol('x22')
x23= Symbol('x23')
x24= Symbol('x24')
x25= Symbol('x25')
x26= Symbol('x26')
x27= Symbol('x27')
x28= Symbol('x28')
x29= Symbol('x29')
x30= Symbol('x30')
x31= Symbol('x31')
x32= Symbol('x32')
x33= Symbol('x33')
x34= Symbol('x34')
x35= Symbol('x35')
x36= Symbol('x36')
x37= Symbol('x37')
x38= Symbol('x38')
x39= Symbol('x39')
x40= Symbol('x40')
x41= Symbol('x41')
x42= Symbol('x42')
x43= Symbol('x43')
x44= Symbol('x44')
x45= Symbol('x45')
x46= Symbol('x46')
x47= Symbol('x47')
x48= Symbol('x48')
x49= Symbol('x49')
x50= Symbol('x50')
x51= Symbol('x51')
x52= Symbol('x52')
x53= Symbol('x53')
x54= Symbol('x54')
x55= Symbol('x55')
x56= Symbol('x56')
x57= Symbol('x57')
x58= Symbol('x58')
x59= Symbol('x59')
x60= Symbol('x60')
x61= Symbol('x61')
x62= Symbol('x62')
x63= Symbol('x63')
x64= Symbol('x64')
x65= Symbol('x65')
x66= Symbol('x66')
x67= Symbol('x67')
x68= Symbol('x68')
x69= Symbol('x69')
x70= Symbol('x70')
x71= Symbol('x71')
x72= Symbol('x72')
x73= Symbol('x73')
x74= Symbol('x74')
x75= Symbol('x75')
x76= Symbol('x76')
x77= Symbol('x77')
x78= Symbol('x78')
x79= Symbol('x79')
x80= Symbol('x80')
print(solve([
x00+x01+x02+x03+x04+x05+x06+x07+x08-25,
x00+x09+x18+x27+x36+x45+x54+x63+x72-25,
x00+x01+x02+x09+x10+x11+x18+x19+x20-25,
x00*x01*x02*x03*x04*x05*x06*x07*x08-10080,
x00*x09*x18*x27*x36*x45*x54*x63*x72-10080,
x00*x01*x02*x09*x10*x11*x18*x19*x20-10080,
x09+x10+x11+x12+x13+x14+x15+x16+x17-25,
x01+x10+x19+x28+x37+x46+x55+x64+x73-25,
x03+x04+x05+x12+x13+x14+x21+x22+x23-25,
x09*x10*x11*x12*x13*x14*x15*x16*x17-10080,
x01*x10*x19*x28*x37*x46*x55*x64*x73-10080,
x03*x04*x05*x12*x13*x14*x21*x22*x23-10080,
x18+x19+x20+x21+x22+x23+x24+x25+x26-25,
x02+x11+x20+x29+x38+x47+x56+x65+x74-25,
x06+x07+x08+x15+x16+x17+x24+x25+x26-25,
x18*x19*x20*x21*x22*x23*x24*x25*x26-10080,
x02*x11*x20*x29*x38*x47*x56*x65*x74-10080,
x06*x07*x08*x15*x16*x17*x24*x25*x26-10080,
x27+x28+x29+x30+x31+x32+x33+x34+x35-25,
x03+x12+x21+x30+x39+x48+x57+x66+x75-25,
x27+x28+x29+x36+x37+x38+x45+x46+x47-25,
x27*x28*x29*x30*x31*x32*x33*x34*x35-10080,
x03*x12*x21*x30*x39*x48*x57*x66*x75-10080,
x27*x28*x29*x36*x37*x38*x45*x46*x47-10080,
x36+x37+x38+x39+x40+x41+x42+x43+x44-25,
x04+x13+x22+x31+x40+x49+x58+x67+x76-25,
x30+x31+x32+x39+x40+x41+x48+x49+x50-25,
x36*x37*x38*x39*x40*x41*x42*x43*x44-10080,
x04*x13*x22*x31*x40*x49*x58*x67*x76-10080,
x30*x31*x32*x39*x40*x41*x48*x49*x50-10080,
x45+x46+x47+x48+x49+x50+x51+x52+x53-25,
x05+x14+x23+x32+x41+x50+x59+x68+x77-25,
x33+x34+x35+x42+x43+x44+x51+x52+x53-25,
x45*x46*x47*x48*x49*x50*x51*x52*x53-10080,
x05*x14*x23*x32*x41*x50*x59*x68*x77-10080,
x33*x34*x35*x42*x43*x44*x51*x52*x53-10080,
x54+x55+x56+x57+x58+x59+x60+x61+x62-25,
x06+x15+x24+x33+x42+x51+x60+x69+x78-25,
x54+x55+x56+x63+x64+x65+x72+x73+x74-25,
x54*x55*x56*x57*x58*x59*x60*x61*x62-10080,
x06*x15*x24*x33*x42*x51*x60*x69*x78-10080,
x54*x55*x56*x63*x64*x65*x72*x73*x74-10080,
x63+x64+x65+x66+x67+x68+x69+x70+x71-25,
x07+x16+x25+x34+x43+x52+x61+x70+x79-25,
x57+x58+x59+x66+x67+x68+x75+x76+x77-25,
x63*x64*x65*x66*x67*x68*x69*x70*x71-10080,
x07*x16*x25*x34*x43*x52*x61*x70*x79-10080,
x57*x58*x59*x66*x67*x68*x75*x76*x77-10080,
x72+x73+x74+x75+x76+x77+x78+x79+x80-25,
x08+x17+x26+x35+x44+x53+x62+x71+x80-25,
x60+x61+x62+x69+x70+x71+x78+x79+x80-25,
x72*x73*x74*x75*x76*x77*x78*x79*x80-10080,
x08*x17*x26*x35*x44*x53*x62*x71*x80-10080,
x60*x61*x62*x69*x70*x71*x78*x79*x80-10080,
x00-7,
x02-5,
x03-6,
x06+1,
x08-4,
x09-6,
x10-4,
x16-2,
x17-7,
x18-1,
x19-2,
x20+1,
x21-4,
x22-7,
x25-5,
x26-6,
x27-2,
x28-5,
x29-1,
x31-6,
x35+1,
x45+1,
x49-5,
x51-2,
x52-6,
x55+1,
x58-3,
x61-7,
x63-5,
x65-2,
x66-7,
x67-4,
x70+1,
x71-3,
x72-3,
x74-7,
x75-5,
x78-4,
x80-2,
1-1
],[x00,x01,x02,x03,x04,x05,x06,x07,x08,x09,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64,x65,x66,x67,x68,x69,x70,x71,x72,x73,x74,x75,x76,x77,x78,x79,x80]))

总结

这是我掌握的第三种通过计算机方式求解数独的第三种方式,前面两种分别是回溯法,DLX算法。
这种多项式求解的方法好处在于将数字之间的逻辑用多项式进行表达,将复杂的逻辑运算交由封装好的工具去处理。
稍微麻烦一点的就是书写多项式的表达式,通过数独的相关信息生成py文件再运行py文件,这是后话了。
该部分会在C#调用Python代码中会有详细介绍了(未完成,预计在2019年1月21日之前会补充这部分内容)。

最终总结

所有结论都需要实际验证正确之后,再形成文章,这是对自己的负责,也是对读者的负责。
也希望读者们能够不吝赐教,表示非常感谢!

迟到的元旦快乐

在元旦之前,本想炫技生成漂亮的数独图案的题目然后发在朋友圈祝朋友们节日快乐。可惜是技术实在不过关。

数独图形

在资深的数独迷眼里,标准数独是指通过盘面上的所有提示数字,有且仅有唯一解。
以下四个图形,虽说有“元旦快乐”的四个字样,但是并不具备唯一解。

我们可以经由DLX算法可以快速得知
以上“元旦快乐”四个数字的可能解的个数是分别是512,8388,66,285。

标准数独的基本条件

盘面至少17个数字。
每一大行中没有两个空行,即第一,二,三行必须有两行存在数字。
每一大列中没有两个空列,即第一,二,三列必须有两列存在数字。
盘面至少有8个不同的已知数字。
字的r3c8会有个2的存在是为了避免第一和第三行可以互换,不满足数独唯一性的必要条件。

求解过程

以元字的表达式为例

1
2
3
4
// 元表达式
var before = new DanceLink().solution_count("000000000001234500000000020134659782000308000000402000000703004009006007070001358");
// 元的第一个已知数据和第二个已知数据交换
var after = new DanceLink().solution_count("000000000002134500000000020134659782000308000000402000000703004009006007070001358");

输出的结果是

1
2
before = 512;
after = 312;

所以最终解可以由after的表达式进行进一步的两两交换去生成。
由因为A的已知数据是30个,所以位置的交换有30*29/2=435种。
整个交换的执行流程如下:
1、建立一个尝试字典集tryDic,键是数独的表达式,值是数独的结果的可能个数。
2、30个位置进行组合,生成435个包含两个位置的集合。
3、数独表达式交换前后分别记为before,after,解的个数分别记作b,a,将before,after及其结果数存入tryDic。
4、对435个集合进行遍历,若a!=0,且a小于b,则before=after;
5、很有可能第一轮排列组合之后,a并不等于1;没有找到唯一解的数独表达式,选取tryDic的结果个数最多的字符串S出来作为下一轮循环的因子。
6、循环执行1~5的过程,注意步骤5中的字符串S应该是过往循环中没有使用过的,否则会陷入死循环。

书上以

1
001000009000200046007080000000001000003000200000500000000030800960007000200000500

这个18个提示数的数独作为例子作为讲解,我也通过以上流程生成了一个18个提示数的标准数独。

借助书上的说法,除了聪明和运气,我们别无他法。

标准数独(元旦快乐)

最终生成的结果如下,难度不大。

逆向思维

由以上位置找固定数独的位置可知,如果标准数独去掉某个提示数,不在构成唯一解,但是满足构成标准数独的基本条件,则可能通过两两交换的生成一个新的标准数独。

参考资料

SqlServer 迁移到mysql

最近有开展Sqlserver 迁移到mysql的工作,以下经验基本上是别的博客没有提到过的,故行此文以作补充。

版本差异

时间精度

Mysql 5.5版本不支持毫秒,Mysql 5.6以上才支持。
时间格式为DateTime(3),当前时间为**Now(3)**。在5.5版本上,这两种写法为提示错误。

row_number函数

mysql 8.0才支持row_number函数,低版本都不支持该函数。
低版本实现方式: 游标查询,或者少量数据的情况下,创建带有自增主键的临时表。

mysql.proc表

该表在mysql 8.0版本中已经不存在了,所以 **select * from mysql.proc;**在mysql 8.0版本中会报错;
需要转换成 **show procedure status where db=’sys’;**这种写法来获取存储过程的相关信息。

with cte

mysql 8.0以上才支持cte,不过Mysql递归cte的写法与sql server存在差异,需要添加关键字recursive,否则会提示cte不存在错误。

如下

1
2
3
4
5
6
7
8
9
with recursive cte as
(
select Id,Pid,DeptName,0 as lvl from Department
where Id = 2
union all
select d.Id,d.Pid,d.DeptName,lvl+1 from cte c inner join Department d
on c.Id = d.Pid
)
select * from cte

.net framework框架

5.6 及以上的的版本使用mysql.data.dll时需要 .net framework 4.5.2以上。

Mysql不具备的SQL Server语法

uniqueidentifier

SqlServer 通过WorkBench 迁移到Mysql时,该字段会自动变成varchar(36)类型。Mysql对应的生成Guid的语法是**uuid()**。
因object不支持强制转换成Guid,所以应将接口代码中的 (Guid)cmd.ExecuteScalar()替换成new Guid(cmd.ExecuteScalar())

deleted,Inserted

SqlServer在执行 insert into table_a select top 10 columnname from table_b时可以通过inserted获取到新插入的十条记录的自增主键,而Mysql不可以。
实现方式 表新增Guid辅助字段,并将Guid放在临时表,通过多表链接查询获取数据。

select top … percent

SqlServer在执行 select top 10 percent columnname from table_a,如果table_a中有200条记录,则会返回前20条记录。
实现方式 Mysql 需要通过获取总记录条数,然后获取总条数*百分比来获取数据。

临时表编码

CREATE TEMPORARY TABLE
temptable1(groupname VARCHAR(50))
DEFAULT CHARSET=utf8;
这样可以解决临时表不能插入中文的问题。

配置文件

默认位置

各版本的配置文件的位置如下:
5.5: C:\Program Files\MySQL\MySQL Server 5.5\my.ini
5.6: C:\ProgramData\MySQL\MySQL Server 5.6\my.ini
5.7: C:\ProgramData\MySQL\MySQL Server 5.7\my.ini
8.0: C:\ProgramData\MySQL\MySQL Server 8.0\my.ini
配置文件修改以后重启mysqlserver才会生效。

longtext存储

max_allowed_packet 的默认值是4M。
设置max_allowed_packet=100M可以解决longtext数据的存储长度的问题。

数据库名大小写

lower_case_table_names
设置lower_case_table_names=1,表名称都设置为统一小写。

id自增不连续

innodb_autoinc_lock_mode=0 解决id自增不连续的问题;

语法差异

不等于

SqlServer的不等于符号 **!=**中间可以存在空格,而Mysql的不可以。
Sql Server: select * from a where a.column ! = 0 不报错
Mysql: select * from a where a.column ! = 0 报错
Mysql: select * from a where a.column != 0 不报错

逻辑取反

SqlServer : update table_a set isOk=~isOk;
Mysql : update table_a set isOk=!isOk;

总结

以上就是本人在做SqlServer 迁移到Mysql的过程中遇到的问题以及相应的解决方案。
如果其中有不正确的地方,强烈欢迎指正,避免误导他人。
希望该文能为你的迁移大业贡献一份力量。

斗地主所有牌型组合个数统计

最近业余时间在研究排列组合的相关问题,话题涉及到了在不考虑花色的情况下?54张牌按照斗地主的方式以20,17,17的方式分给3个人有多少中可能解决方案?
我觉得这个问题有点意思,并以sql脚本的形式实现了问题的求解过程,希望大家能从这篇文章中所有收获。

问题分析

以4个3为例,分给地主1,农民2,农民3的所有组合情况如下:
4个3的组合情况
既4个3在3个玩家中的可能情况有15种。在不考虑斗地主手牌数限制的情况下,因A到K有13张牌,则A到K的所有位置的组合数有$15^{13}$种可能性。
以大王为例,分给地主1,农民2,农民3的所有组合情况如下:
大王的组合情况
在不考虑斗地主手牌数限制的情况下,大王和小王在三家的可能性有3*3=9种可能性。
所以牌型组合的理论上限是$9*15^{13}$。接近于$1.75*10^{16}$。

组合情况的一种
根据计数原理,结合上图可知:
如果37在玩家1,2,3的手牌张数分别是20,0,0的可行方法是1种。
如果8
Q在玩家1,2,3的手牌张数分别是0,17,3的可行方法是35种。
如果K到大王在玩家1,2,3的手牌张数分别是0,0,14的可行方法是1种。
所以三个步骤组合起来的总可行方法是$1*35*1=35$种方法。

具体细节参考Sql server版斗地主牌型组合统计的代码,最终的出来的结果是138712181744994,即$1.3871*10^{14}$。即在不区分花色的情况下,斗地主所有的牌型组合数。

总结

记录斗地主的所有牌型这种方式来做AI是不切实际的,毕竟普通PC机是没有办法存储$1.3871*10^{14}$条数据的。


参考资料

m个球放到n个箱子中几种解法

该文的诞生源于一个问题:斗地主有多少种牌型组合?
众所周知,斗地主共54张牌分别给三个人20,17,17张牌,因为方块3和梅花3在斗地主中无区别,转化一下问题就是13种不同的颜色(不包括黑白)的球各4个,黑球,白球各1个,放到容积分别为20,17,17的三个箱子中有多少种方法?
第一想法是想到采取排列组合的方式去解,但是多久没有做排列组合的习题,我发现3红3黄3蓝1黑1白共11个球放到容积为5,3,3的三个箱子中,不考虑顺序总共有多少种方法?这个问题我都计算困难,该文简单讲一下该问题的两种解法。

正文

方案1:全排列

方案1.1 考虑黑白球

假设11个球的颜色全不相同,则所有排列顺序是1~11的全排列是11!=39916800
再假设1,4,7为红球,2,5,8为黄球,3,6,9为蓝球,10为黑球,11为白球。
也可以1,2,3为红球,4,5,6为黄球,7,8,9为蓝球,10为白球,11为黑球。给球编号是为了计算全排列方便。
以全排列元素中的一个链表X(全排列的取值之一) **{9,8,3,5,6,4,11,7,1,2,10}**为例,
可以分成 **{9,8,3,5,6}**和 **{4,11,7}**和 **{1,2,10}三组。
将每组数据中≤9的对3取余,结果变成了A:
{0,2,0,2,0}和B:{1,1,11}和C:{1,2,10}**三组数据。
∵ 在箱子中 **{4,11,7}**和 {4,7,11}是等价的。
∴ A、B、C三组都需要排序,排序后为{0,0,0,2,2},{1,1,11},{1,2,10}
{9,8,3,5,6,4,11,7,1,2,10}
表示A箱子有3个蓝球,2个黄球;B箱子有2个红球,1个白球;C箱子有1个红球,1个黄球,1个黑球。
∴链表X的表达式是0,0,0,2,2,1,1,11,1,2,10
∴将39916800链表的表达式去重就可以算出序中提到的问题的解为355个。

方案1.2 先不考虑黑白球

∵黑球的可能位置只有3种,白球的可能位置也只有3种。
即黑白球都在A箱,都在B箱,都在C箱,分别在AC箱,分别在BC箱,分别在AB箱。
所有组合情况如下
黑白球位置组合
这样就只需要计算9的全排列9!=362880次数据。
计算方式如方案1.1,再次不再赘述。
结果依旧为355。
具体计算代码请参考方案一代码

方案2:笛卡尔积

在不考虑各箱子容积的前提下;
每个球都有3个箱子可以选择,则11个球的位置有3的11次方=177147中可能性。
若箱子A,B,C编号为{1,2,3}。大小分别为{5,3,3}。
则第一个球和第二球的箱子可能组合是{1,2,3}和{1,2,3}的笛卡尔积

1
{   {1,1},{1,2},{1,3},   {2,1},{2,2},{2,3},   {3,1},{3,2},{3,3}  }  

将每个球所有的可能性做笛卡尔积之后,会得到一个177147个元素个数为11的链表L的集合S。
若链表L中有5个元素1,有3个元素2,有3个元素3。则链表L符合球进入箱子的逻辑。否则不符合球进入箱子的逻辑。
假若链表L为{1,2,3,2,3,2,1,1,1,1,3}且11个球的顺序为红,红,红,黄,黄,黄,蓝,蓝,蓝,黑,白
则箱子内的颜色情况如下:
A箱:红蓝蓝蓝黑
B箱:红黄蓝
C箱:红黄白
所以链表L的表示式为 A-红蓝蓝蓝黑 B-红黄蓝 C-红黄白
将所有符合进箱逻辑的链表的表达式算出来去重,算出来结果依旧为355。
具体计算代码请参考方案二代码

注意:相同颜色的球的顺序应该连在一起,否则箱子内的球需要再做一次排序去重。

总结

该文是对排列组合知识的一个补充。如果有更好的解决m个不同颜色的球放到n个箱子中的更好的解法,欢迎留言或者直接联系我。
如果能有算出斗地主有多少种牌型组合的方法,则更希望能联系我。
方案2在箱子不够多,球不够多的情况下,的确是不错的一个计算方案。


参考资料

python画爱心

该文算是python版的”Hello world”初探,故作相关的记录。
环境准备:Visual Studio 2017,python

正文

爱心一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np
import matplotlib.pyplot as plt

x_coords = np.linspace(- 100, 100, 500)
y_coords = np.linspace(- 100, 100, 500)
points = []

for y in y_coords:
for x in x_coords:
if((x* 0.03)** 2+(y* 0.03)** 2- 1)** 3-(x* 0.03)** 2*(y* 0.03)** 3<= 0:
points.append({ "x": x, "y": y})

heart_x = list(map( lambda point: point[ "x"], points))
heart_y = list(map( lambda point: point[ "y"], points))

plt.scatter(heart_x, heart_y, s= 10, alpha= 0.5)
plt.show()

效果如图
爱心1

爱心二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import turtle
import time

# 画心形圆弧
def hart_arc():
for i in range(200):
turtle.right(1)
turtle.forward(2)

def move_pen_position(x, y):
turtle.hideturtle() # 隐藏画笔(先)
turtle.up() # 提笔
turtle.goto(x, y) # 移动画笔到指定起始坐标(窗口中心为0,0)
turtle.down() # 下笔
turtle.showturtle() # 显示画笔


#love = input("请输入表白话语,默认为‘I Love You’:")
#signature = input("请签署你的大名,不填写默认不显示:")

signature= ''
love = 'I Love You'

if love == '':
love = 'I Love You'

# 初始化
turtle.setup(width=800, height=500) # 窗口(画布)大小
turtle.color('red', 'pink') # 画笔颜色
turtle.pensize(3) # 画笔粗细
turtle.speed(1) # 描绘速度
# 初始化画笔起始坐标
move_pen_position(x=0,y=-180) # 移动画笔位置
turtle.left(140) # 向左旋转140度

turtle.begin_fill() # 标记背景填充位置

# 画心形直线( 左下方 )
turtle.forward(224) # 向前移动画笔,长度为224
# 画爱心圆弧
hart_arc() # 左侧圆弧
turtle.left(120) # 调整画笔角度
hart_arc() # 右侧圆弧
# 画心形直线( 右下方 )
turtle.forward(224)

turtle.end_fill() # 标记背景填充结束位置

# 在心形中写上表白话语
move_pen_position(0,0) # 表白语位置
turtle.hideturtle() # 隐藏画笔
turtle.color('#CD5C5C', 'pink') # 字体颜色
# font:设定字体、尺寸(电脑下存在的字体都可设置) align:中心对齐
turtle.write(love, font=('Arial', 30, 'bold'), align="center")

# 签写署名
if signature != '':
turtle.color('red', 'pink')
time.sleep(2)
move_pen_position(180, -180)
turtle.hideturtle() # 隐藏画笔
turtle.write(signature, font=('Arial', 20), align="center")

# 点击窗口关闭程序
window = turtle.Screen()
window.exitonclick()

效果如下图
爱心2

爱心三

1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import numpy as np

x=np.linspace(-2,2,200)
y1 =np.sqrt(1-np.square(np.fabs(x)-1))
y2 =np.arccos(1-np.fabs(x))-np.pi

plt.plot(x,y1,'r',x,y2,'r')
plt.axis([-2.5,2.5,-3.5,1.5])

plt.title('hello world of python,copy from @andrew',fontsize=16)
plt.show()

效果如下图
爱心3

遇到问题

在visual studio 2017中命令行执行 pip install numpy 无效
在nuget程序包管理器控制台执行即可
pip install pyinstaller之后生成的exe无法执行
在我电脑上的路径是C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python36_64\Scripts,
把run.py拷贝到该目录,执行pyinstaller -F -W p . run.py解决问题

总结

暴击


参考资料

手写数独教程

该文目的是简单记载3*3数独的手写过程,并熟悉一下mathjax工具的运用。

正文

该文的整个过程可以用手写实现,也可以用编程语言实现,该文不对怎么用编程语言实现做详细描述。仅阐述手写的详细过程:

生成行

首先,我们可以手写一行包含19数据行A,不重复即可,顺序不重要,该文以19顺序排列做示例。
\begin{bmatrix}
1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \
\end{bmatrix}
然后将数据行A的前三位1,2,3移到数据行A的末尾,生成新的一行数据行B:
\begin{bmatrix}
4 & 5 & 6 & 7 & 8 & 9 &1 & 2 & 3 \
\end{bmatrix}
然后将数据行B的前三位4,5,6移到数据行B的末尾,生成新的一行数据行C:
\begin{bmatrix}
7 & 8 & 9 &1 & 2 & 3 & 4 & 5 & 6 \
\end{bmatrix}

生成块

再将数据行依次组合,生成一个3行9列的数据块 $X_1$
\begin{bmatrix}
1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \
4 & 5 & 6 & 7 & 8 & 9 & 1 & 2 & 3 \
7 & 8 & 9 & 1 & 2 & 3 & 4 & 5 & 6 \
\end{bmatrix}
将数据块$X_1$的第一列
\begin{bmatrix}
1 \
4 \
7 \
\end{bmatrix}
移到最后一列生成新的数据块$X_2$
\begin{bmatrix}
2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 1\
5 & 6 & 7 & 8 & 9 & 1 & 2 & 3 & 4\
8 & 9 & 1 & 2 & 3 & 4 & 5 & 6 & 7\
\end{bmatrix}
再将数据块$X_2$的第一列
\begin{bmatrix}
2 \
5 \
8 \
\end{bmatrix}
移到最后一列生成新的数据块$X_3$
\begin{bmatrix}
3 & 4 & 5 & 6 & 7 & 8 & 9 & 1 & 2\
6 & 7 & 8 & 9 & 1 & 2 & 3 & 4 & 5\
9 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8\
\end{bmatrix}
再将数据块$X_1$,$X_2$,$X_3$依次组合,就形成了如下图所示的数独$Y_1$。
\begin{bmatrix}
1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \
4 & 5 & 6 & 7 & 8 & 9 & 1 & 2 & 3 \
7 & 8 & 9 & 1 & 2 & 3 & 4 & 5 & 6 \
2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 1 \
5 & 6 & 7 & 8 & 9 & 1 & 2 & 3 & 4 \
8 & 9 & 1 & 2 & 3 & 4 & 5 & 6 & 7 \
3 & 4 & 5 & 6 & 7 & 8 & 9 & 1 & 2 \
6 & 7 & 8 & 9 & 1 & 2 & 3 & 4 & 5 \
9 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \
\end{bmatrix}
所以说手写数独是很简单的,那怎么样让别人看不出来这是一个手写的?

行交换

可以将 **[1,2,3]**行中两两任意互换;
可以将 **[4,5,6]**行中两两任意互换;
可以将 **[7,8,9]**行中两两任意互换;
第一行和第三行交换用 $1\Leftrightarrow3$ 表示。
依次执行$1\Leftrightarrow3$ ,$4\Leftrightarrow5$ 和 $8\Leftrightarrow9$
生成新的数独$Y_2$。如下:
\begin{bmatrix}
4 & 5 & 6 & 7 & 8 & 9 & 1 & 2 & 3 \
7 & 8 & 9 & 1 & 2 & 3 & 4 & 5 & 6 \
1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \
5 & 6 & 7 & 8 & 9 & 1 & 2 & 3 & 4 \
2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 1 \
8 & 9 & 1 & 2 & 3 & 4 & 5 & 6 & 7 \
3 & 4 & 5 & 6 & 7 & 8 & 9 & 1 & 2 \
9 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \
6 & 7 & 8 & 9 & 1 & 2 & 3 & 4 & 5 \
\end{bmatrix}

列交换

可以将 **[1,2,3]**列中两两任意互换;
可以将 **[4,5,6]**列中两两任意互换;
可以将 **[7,8,9]**列中两两任意互换;
第一列和第三列交换用 $1\Longleftrightarrow3$ 表示。
依次对$Y_2$执行$1\Longleftrightarrow3$ ,$4\Longleftrightarrow5$ 和 $8\Longleftrightarrow9$
生成新的数独$Y_3$。如下:
\begin{bmatrix}
6 & 5 & 4 & 8 & 7 & 9 & 1 & 3 & 2 \
9 & 8 & 7 & 2 & 1 & 3 & 4 & 6 & 5 \
3 & 2 & 1 & 5 & 4 & 6 & 7 & 9 & 8 \
7 & 6 & 5 & 9 & 8 & 1 & 2 & 4 & 3 \
4 & 3 & 2 & 6 & 5 & 7 & 8 & 1 & 9 \
1 & 9 & 8 & 3 & 2 & 4 & 5 & 7 & 6 \
5 & 4 & 3 & 7 & 6 & 8 & 9 & 2 & 1 \
2 & 1 & 9 & 4 & 3 & 5 & 6 & 8 & 7 \
8 & 7 & 6 & 1 & 9 & 2 & 3 & 5 & 4 \
\end{bmatrix}

数值交换

将数独$Y_3$中的数字任意两个数字互换局组成了新的数独,如数独$Y_3$的27互换,就生成了新的数独$Y_4$
\begin{bmatrix}
6 & 5 & 4 & 8 & 2 & 9 & 1 & 3 & 7 \
9 & 8 & 2 & 7 & 1 & 3 & 4 & 6 & 5 \
3 & 7 & 1 & 5 & 4 & 6 & 2 & 9 & 8 \
2 & 6 & 5 & 9 & 8 & 1 & 7 & 4 & 3 \
4 & 3 & 7 & 6 & 5 & 2 & 8 & 1 & 9 \
1 & 9 & 8 & 3 & 7 & 4 & 5 & 2 & 6 \
5 & 4 & 3 & 2 & 6 & 8 & 9 & 7 & 1 \
7 & 1 & 9 & 4 & 3 & 5 & 6 & 8 & 2 \
8 & 2 & 6 & 1 & 9 & 7 & 3 & 5 & 4 \
\end{bmatrix}

行交换,列交换,数值交换次数越多,就越来越没有手写的痕迹。掌握了数独的一个相关规律,手写数独就再是遥不可及的事情。

总结

该文简单地介绍了手写数独的过程,希望那些对数独感兴趣的人读了这篇文章后有所收获。


参考资料

Hexo画流程图

该文简单介绍一下在Hexo博客上画流程图。

正文

mermaid

安装代码

1
npm install hexo-filter-mermaid-diagrams

打开themes/next/_config.yml
添加以下代码

1
2
3
4
5
6
# mermaid chart
mermaid: ## mermaid url https://github.com/knsv/mermaid
enable: true # default true
version: "7.1.2" # default v7.1.2
options: # find more api options from https://github.com/knsv/mermaid/blob/master/src/mermaidAPI.js
#startOnload: true // default true

themes/next/layout/_partials/footer.swig最后加上

1
2
3
4
5
6
7
8
{% if (theme.mermaid.enable)  %}
<script src='https://cdnjs.cloudflare.com/ajax/libs/mermaid/7.1.2/mermaid.min.js'></script>
<script>
if (window.mermaid) {
mermaid.initialize({theme: 'forest'});
}
</script>
{% endif %}

最后一步修改根目录的_config.yml,把external_link的值改为false,默认的为true,这一步绝大多数教程中都没有写。

mermaid画流程图
示例代码

```mermaid
graph TD;
起床–>洗漱;
洗漱–>吃早餐;
洗漱–>不吃早餐;
吃早餐–>出门;
不吃早餐–>出门;
```

效果如下

graph TD;
    起床-->洗漱;
    洗漱-->吃早餐;
    洗漱-->不吃早餐;
    吃早餐-->出门;
    不吃早餐-->出门;

flow

安装代码

1
npm install --save hexo-filter-flowchart

然后无需其余配置
flow画流程图示例代码如下

```flow
st=>start: Start|past:>http://www.google.com[blank]
e=>end: End:>http://www.google.com
op1=>operation: My Operation|past
op2=>operation: Stuff|current
sub1=>subroutine: My Subroutine|invalid
cond=>condition: Yes
or No?|approved:>http://www.google.com
c2=>condition: Good idea|rejected
io=>inputoutput: catch something…|request
st->op1(right)->cond
cond(yes, right)->c2
cond(no)->sub1(left)->op1
c2(yes)->io->e
c2(no)->op2->e
```

flow画流程图效果如下

sequence

安装代码

1
npm install --save hexo-filter-sequence

然后无需其余配置。
sequence 画流程图示例代码

```sequence
Alice->Bob: Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob–>Alice: I am good thanks!
```

sequence 画流程图效果如下

总结

相比之下,mermaid的语法最简洁,渲染成图的过程最复杂,配置也最复杂。
而flow和sequence相对而言配置简单一点,但是语法复杂一些。


参考资料

  • 如何让你的HEXO博客支持手写流程图?

hexo 调用share.js

我本来想做的分享按钮预期效果如下:
分享图标,本来也打开了一个实现了该效果的个人网站,结果一不小心关掉了,然后就只能自己动手制作分享功能。

正文

首先在themes\next\layout中新建一个文件socialshare.swig
编辑内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script src="../lib/jquery/index.js"></script>
<link href="https://cdn.bootcss.com/social-share.js/1.0.16/css/share.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/social-share.js/1.0.16/js/jquery.share.min.js"></script>

<script> var $config = {
url : window.location.href,// 网址,默认使用 window.location.href
source : '', // 来源(QQ空间会用到), 默认读取head标签:<meta name="site" content="http://overtrue" />
title : '', // 标题,默认读取 document.title 或者 <meta name="title" content="share.js" />
description : '', // 描述, 默认读取head标签:<meta name="description" content="PHP弱类型的实现原理分析" />
image : '', // 图片, 默认取网页中第一个img标签
sites : ['qzone', 'qq', 'weibo','wechat'], // 启用的站点
disabled : ['google', 'facebook', 'twitter'], // 禁用的站点
wechatQrcodeTitle : '微信扫一扫:分享', // 微信二维码提示文字
wechatQrcodeHelper : '<p>微信里点“发现”,扫一下</p><p>二维码便可将本文分享至朋友圈。</p>',
target : '_blank' //打开方式
};
$('.social-share').share($config);
</script>

然后找到themes\next\layout中的文件post.swig中的这部分代码

1
<footer class="post-footer">

之前贴上以下代码

1
2
3
4
{% if theme.social_share and not is_index %}
{% include '../_partials/share/socialshare.swig' %}
<div class="social-share"></div>
{% endif %}

在主题_config.yml文件中增加以下代码

1
2
social_share:
enable: true

保存修改后,然后hexo clean ,hexo g ,hexo d 即可看到点击效果。

总结

虽说分享功能不如我的预期完整,但是也是解决了baidu share组件和赞赏组件冲突的问题。
如果有时间,还有兴趣的时候会继续折腾Share.js这个组件。
然后希望有已经部署成功的人指点一二,不胜感激。

后记

于2018年12月14日修正并解决了该问题。


参考资料