上一次我们使用遗传算法求解了一个较为复杂的多元非线性函数的极值问题,也基本了解了遗传算法的实现基本步骤。这一次,我再以经典的TSP问题为例,更加深入地说明遗传算法中选择、交叉、变异等核心步骤的实现。而且这一次解决的是离散型问题,上一次解决的是连续型问题,刚好形成对照。

首先介绍一下TSP问题。TSP(traveling salesman problem,旅行商问题)是典型的NP完全问题,即其最坏情况下的时间复杂度随着问题规模的增大按指数方式增长,到目前为止还没有找到一个多项式时间的有效算法。TSP问题可以描述为:已知n个城市之间的相互距离,某一旅行商从某一个城市出发,访问每个城市一次且仅一次,最后回到出发的城市,如何安排才能使其所走的路线最短。换言之,就是寻找一条遍历n个城市的路径,或者说搜索自然子集X={1,2,...,n}(X的元素表示对n个城市的编号)的一个排列P(X)={V1,V2,....,Vn},使得Td=∑d(Vi,Vi+1)+d(Vn,V1)取最小值,其中,d(Vi,Vi+1)表示城市Vi到Vi+1的距离。TSP问题不仅仅是旅行商问题,其他许多NP完全问题也可以归结为TSP问题,如邮路问题,装配线上的螺母问题和产品的生产安排问题等等,也使得TSP问题的求解具有更加广泛的实际意义。

  再来说针对TSP问题使用遗传算法的步骤。

  (1)编码问题:由于这是一个离散型的问题,我们采用整数编码的方式,用1~n来表示n个城市,1~n的任意一个排列就构成了问题的一个解。可以知道,对于n个城市的TSP问题,一共有n!种不同的路线。

  (2)种群初始化:对于N个个体的种群,随机给出N个问题的解(相当于是染色体)作为初始种群。这里具体采用的方法是:1,2,...,n作为第一个个体,然后2,3,..n分别与1交换位置得到n-1个解,从2开始,3,4,...,n分别与2交换位置得到n-2个解,依次类推。(如果这样还不够初始种群的数量,可以再考虑n,n-1,...,1这个序列,然后再按照相同的方法生成,等等)

  (3)适应度函数:设一个解遍历初始行走的总距离为D,则适应度fitness=1/D.即总距离越高,适应度越低,总距离越低(解越好),适应度越高。

  (4) 选择操作:个体被选中的概率与适应度成正比,适应度越高,个体被选中的概率越大。这里仍然采用轮盘赌法。

  (5) 交叉操作:交叉操作是遗传算法最重要的操作,是产生新个体的主要来源,直接关系到算法的全局寻优能力,这里采用部分映射交叉。比如对于n=10的情况,对于两个路径:            1 2 4 5 6 3   9 10 8 7

3 9 7 6 8 10 5  1  2  4

随机产生两个[1,10]之间的随机数r1,r2,代表选择交叉的位置,比如r1=2,r2=4,如上图标红的位置,将第一个个体r1到r2之间的基因(即城市序号)与第二个个体r1到r2之间的基因交换,交换之后变为:

1 9 7 6 6 3   9 10 8 7

3 2 4 5 8 10 5  1  2  4

黄色部分表示交叉过来的基因,这个时候会发现可能交叉过来的基因与原来其他位置上的基因有重复,容易直到,第一个个体重复基因的数目与第二个个体重复基因的数目是相同的(这里都是3个),只需要把第一个个体重复的基因(用绿色标识)与第二个个体重复的基因做交换,即可以消除冲突。消除冲突之后的解如下:

1 9 7 6 5 3   2 10 8 4

3 2 4 5 8 10 6  1  9  7

(6)变异操作:变异操作采取对于一个染色体(即个体)随机选取两个基因进行交换的策略。比如对于染色体:

2 4 6 10 3 1 9 7 8 5

随机选取了两个位置p1=3,p2=8(标红位置),交换这两个位置的基因,得到新的染色体为:

2 4 7 10 3 1 9 6 8 5

(7) 进化逆转操作:这个是标准的遗传算法没有的,是我们为了加速进化而加入的一个操作。这里的进化是指逆转操作具有单向性,即只有逆转之后个体变得更优才会执行逆转操作,否则逆转无效。具体的方法是,随机产生[1,10](这里仍然以10个城市为例)之间的两个随机数r1和r2(其实也是允许相同的,只是r1,r2相同之后,逆转自然无效,设置交叉变异都是无效的,但是这不会经常发生),然后将r1和r2之间的基因进行反向排序。比如对于染色体:

1 3 4 2 10  9 8 7 6 5

r1=3,r2=5,它们之间的基因(标红位置)反向排列之后得到的染色体如下:

1 3 10 2 4  9 8 7 6 5

根据以上的步骤,我们就可以比较容易写出用遗传算法求解TSP问题的具体代码了,这里仍然使用C语言。先以规模比较小的城市为例,这里取14个,城市之间的距离会直接在代码中给出。代码如下:

  1. /*
  2. *遗传算法(GA) 解决TSP 问题
  3. *案例参考自《MATLAB 智能算法30个案例分析》
  4. *本例以14个城市为例,14个城市的位置坐标如下(括号内第一个元素为X坐标,第二个为纵坐标):1:(16.47,96.10) 2:(16.47,94.44) 3:(20.09,92.54)
  5. *4:(22.39,93.37) 5:(25.23,97.24) 6:(22.00,96.05) 7:(20.47,97.02) 8:(17.20,96.29) 9:(16.30,97.38) 10:(14.05,98.12) 11:(16.53,97.38)
  6. *12:(21.52,95.59) 13:(19.41,97.13) 14:(20.09,92.55)
  7. *遗传算法实现的步骤为:(1)编码 (2) 种群初始化 (3) 构造适应度函数 (4) 选择操作 (5) 交叉操作 (6) 变异操作 (7) 进化逆转操作
  8. * 具体实现的步骤这里不详细说,参考《MATLAB 智能算法30个案例分析》P38 - P40
  9. * update in 16/12/4
  10. * author:Lyrichu
  11. * email:919987476@qq.com
  12. */
  13. #include<stdio.h>
  14. #include<stdlib.h>
  15. #include<math.h>
  16. #include<time.h>
  17. #define maxgen 200 // 最大进化代数
  18. #define sizepop 100 // 种群数目
  19. #define pcross 0.6 // 交叉概率
  20. #define pmutation 0.1 // 变异概率
  21. #define lenchrom 14 // 染色体长度(这里即为城市个数)
  22. double city_pos[lenchrom][2] = {{16.47,96.10},{16.47,94.44},{20.09,92.54},{22.39,93.37},{25.23,97.24},{22.00,96.05},{20.47,97.02},
  23. {17.20,96.29},{16.30,97.38},{14.05,98.12},{16.53,97.38},{21.52,95.59},{19.41,97.13},{20.09,92.55}}; // 定义二维数组存放14个城市的X、Y坐标
  24. int chrom[sizepop][lenchrom]; // 种群
  25. int best_result[lenchrom]; // 最佳路线
  26. double min_distance; // 最短路径长度
  27.  
  28. // 函数声明
  29. void init(void); // 种群初始化函数
  30. double distance(double *,double *); // 计算两个城市之间的距离
  31. double * min(double *); // 计算距离数组的最小值
  32. double path_len(int *); // 计算某一个方案的路径长度,适应度函数为路线长度的倒数
  33. void Choice(int [sizepop][lenchrom]); // 选择操作
  34. void Cross(int [sizepop][lenchrom]); // 交叉操作
  35. void Mutation(int [sizepop][lenchrom]); // 变异操作
  36. void Reverse(int [sizepop][lenchrom]); // 逆转操作
  37.  
  38. // 种群初始化
  39. void init(void)
  40. {
  41. int num = 0;
  42. while(num < sizepop)
  43. {
  44. for(int i=0;i<sizepop;i++)
  45. for(int j=0;j<lenchrom;j++)
  46. chrom[i][j] = j+1;
  47. num++;
  48. for(int i=0;i<lenchrom-1;i++)
  49. {
  50. for(int j=i+1;j<lenchrom;j++)
  51. {
  52. int temp = chrom[num][i];
  53. chrom[num][i] = chrom[num][j];
  54. chrom[num][j] = temp; // 交换第num个个体的第i个元素和第j个元素
  55. num++;
  56. if(num >= sizepop)
  57. break;
  58. }
  59. if(num >= sizepop)
  60. break;
  61. }
  62. // 如果经过上面的循环还是无法产生足够的初始个体,则随机再补充一部分
  63. // 具体方式就是选择两个基因位置,然后交换
  64. while(num < sizepop)
  65. {
  66. double r1 = ((double)rand())/(RAND_MAX+1.0);
  67. double r2 = ((double)rand())/(RAND_MAX+1.0);
  68. int p1 = (int)(lenchrom*r1); // 位置1
  69. int p2 = (int)(lenchrom*r2); // 位置2
  70. int temp = chrom[num][p1];
  71. chrom[num][p1] = chrom[num][p2];
  72. chrom[num][p2] = temp; // 交换基因位置
  73. num++;
  74. }
  75. }
  76. }
  77.  
  78. // 距离函数
  79. double distance(double * city1,double * city2)
  80. {
  81. double x1 = *city1;
  82. double y1 = *(city1+1);
  83. double x2 = *(city2);
  84. double y2 = *(city2+1);
  85. double dis = sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
  86. return dis;
  87. }
  88. // min()函数
  89. double * min(double * arr)
  90. {
  91. static double best_index[2];
  92. double min_dis = *arr;
  93. double min_index = 0;
  94. for(int i=1;i<sizepop;i++)
  95. {
  96. double dis = *(arr+i);
  97. if(dis < min_dis)
  98. {
  99. min_dis = dis;
  100. min_index = i;
  101. }
  102. }
  103. best_index[0] = min_index;
  104. best_index[1] = min_dis;
  105. return best_index;
  106. }
  107.  
  108. // 计算路径长度
  109. double path_len(int * arr)
  110. {
  111. double path = 0; // 初始化路径长度
  112. int index = *arr; // 定位到第一个数字(城市序号)
  113. for(int i=0;i<lenchrom-1;i++)
  114. {
  115. int index1 = *(arr+i);
  116. int index2 = *(arr+i+1);
  117. double dis = distance(city_pos[index1-1],city_pos[index2-1]);
  118. path += dis;
  119. }
  120. int last_index = *(arr+lenchrom-1); // 最后一个城市序号
  121. int first_index = *arr; // 第一个城市序号
  122. double last_dis = distance(city_pos[last_index-1],city_pos[first_index-1]);
  123. path = path + last_dis;
  124. return path; // 返回总的路径长度
  125. }
  126.  
  127. // 选择操作
  128. void Choice(int chrom[sizepop][lenchrom])
  129. {
  130. double pick;
  131. double choice_arr[sizepop][lenchrom];
  132. double fit_pro[sizepop];
  133. double sum = 0;
  134. double fit[sizepop]; // 适应度函数数组(距离的倒数)
  135. for(int j=0;j<sizepop;j++)
  136. {
  137. double path = path_len(chrom[j]);
  138. double fitness = 1/path;
  139. fit[j] = fitness;
  140. sum += fitness;
  141. }
  142. for(int j=0;j<sizepop;j++)
  143. {
  144. fit_pro[j] = fit[j]/sum; // 概率数组
  145. }
  146. // 开始轮盘赌
  147. for(int i=0;i<sizepop;i++)
  148. {
  149. pick = ((double)rand())/RAND_MAX; // 0到1之间的随机数
  150. for(int j=0;j<sizepop;j++)
  151. {
  152. pick = pick - fit_pro[j];
  153. if(pick<=0)
  154. {
  155. for(int k=0;k<lenchrom;k++)
  156. choice_arr[i][k] = chrom[j][k]; // 选中一个个体
  157. break;
  158. }
  159. }
  160.  
  161. }
  162. for(int i=0;i<sizepop;i++)
  163. {
  164. for(int j=0;j<lenchrom;j++)
  165. chrom[i][j] = choice_arr[i][j];
  166. }
  167. }
  168.  
  169. //交叉操作
  170. void Cross(int chrom[sizepop][lenchrom])
  171. {
  172. double pick;
  173. double pick1,pick2;
  174. int choice1,choice2;
  175. int pos1,pos2;
  176. int temp;
  177. int conflict1[lenchrom]; // 冲突位置
  178. int conflict2[lenchrom];
  179. int num1,num2;
  180. int index1,index2;
  181. int move = 0; // 当前移动的位置
  182. while(move<lenchrom-1)
  183. {
  184. pick = ((double)rand())/RAND_MAX; // 用于决定是否进行交叉操作
  185. if(pick > pcross)
  186. {
  187. move += 2;
  188. continue; // 本次不进行交叉
  189. }
  190. // 采用部分映射杂交
  191. choice1 = move; // 用于选取杂交的两个父代
  192. choice2 = move+1; // 注意避免下标越界
  193. pick1 = ((double)rand())/(RAND_MAX+1.0);
  194. pick2 = ((double)rand())/(RAND_MAX+1.0);
  195. pos1 = (int)(pick1*lenchrom); // 用于确定两个杂交点的位置
  196. pos2 = (int)(pick2*lenchrom);
  197. while(pos1 > lenchrom -2 || pos1 < 1)
  198. {
  199. pick1 = ((double)rand())/(RAND_MAX+1.0);
  200. pos1 = (int)(pick1*lenchrom);
  201. }
  202. while(pos2 > lenchrom -2 || pos2 < 1)
  203. {
  204. pick2 = ((double)rand())/(RAND_MAX+1.0);
  205. pos2 = (int)(pick2*lenchrom);
  206. }
  207. if(pos1 > pos2)
  208. {
  209. temp = pos1;
  210. pos1 = pos2;
  211. pos2 = temp; // 交换pos1和pos2的位置
  212. }
  213. for(int j=pos1;j<=pos2;j++)
  214. {
  215. temp = chrom[choice1][j];
  216. chrom[choice1][j] = chrom[choice2][j];
  217. chrom[choice2][j] = temp; // 逐个交换顺序
  218. }
  219. num1 = 0;
  220. num2 = 0;
  221. if(pos1 > 0 && pos2 < lenchrom-1)
  222. {
  223. for(int j =0;j<=pos1-1;j++)
  224. {
  225. for(int k=pos1;k<=pos2;k++)
  226. {
  227. if(chrom[choice1][j] == chrom[choice1][k])
  228. {
  229. conflict1[num1] = j;
  230. num1++;
  231. }
  232. if(chrom[choice2][j] == chrom[choice2][k])
  233. {
  234. conflict2[num2] = j;
  235. num2++;
  236. }
  237. }
  238. }
  239. for(int j=pos2+1;j<lenchrom;j++)
  240. {
  241. for(int k=pos1;k<=pos2;k++)
  242. {
  243. if(chrom[choice1][j] == chrom[choice1][k])
  244. {
  245. conflict1[num1] = j;
  246. num1++;
  247. }
  248. if(chrom[choice2][j] == chrom[choice2][k])
  249. {
  250. conflict2[num2] = j;
  251. num2++;
  252. }
  253. }
  254.  
  255. }
  256. }
  257. if((num1 == num2) && num1 > 0)
  258. {
  259. for(int j=0;j<num1;j++)
  260. {
  261. index1 = conflict1[j];
  262. index2 = conflict2[j];
  263. temp = chrom[choice1][index1]; // 交换冲突的位置
  264. chrom[choice1][index1] = chrom[choice2][index2];
  265. chrom[choice2][index2] = temp;
  266. }
  267. }
  268. move += 2;
  269. }
  270. }
  271.  
  272. // 变异操作
  273. // 变异策略采取随机选取两个点,将其对换位置
  274. void Mutation(int chrom[sizepop][lenchrom])
  275. {
  276. double pick,pick1,pick2;
  277. int pos1,pos2,temp;
  278. for(int i=0;i<sizepop;i++)
  279. {
  280. pick = ((double)rand())/RAND_MAX; // 用于判断是否进行变异操作
  281. if(pick > pmutation)
  282. continue;
  283. pick1 = ((double)rand())/(RAND_MAX+1.0);
  284. pick2 = ((double)rand())/(RAND_MAX+1.0);
  285. pos1 = (int)(pick1*lenchrom); // 选取进行变异的位置
  286. pos2 = (int)(pick2*lenchrom);
  287. while(pos1 > lenchrom-1)
  288. {
  289. pick1 = ((double)rand())/(RAND_MAX+1.0);
  290. pos1 = (int)(pick1*lenchrom);
  291. }
  292. while(pos2 > lenchrom-1)
  293. {
  294. pick2 = ((double)rand())/(RAND_MAX+1.0);
  295. pos2 = (int)(pick2*lenchrom);
  296. }
  297. temp = chrom[i][pos1];
  298. chrom[i][pos1] = chrom[i][pos2];
  299. chrom[i][pos2] = temp;
  300. }
  301. }
  302.  
  303. // 进化逆转操作
  304. void Reverse(int chrom[sizepop][lenchrom])
  305. {
  306. double pick1,pick2;
  307. double dis,reverse_dis;
  308. int n;
  309. int flag,pos1,pos2,temp;
  310. int reverse_arr[lenchrom];
  311.  
  312. for(int i=0;i<sizepop;i++)
  313. {
  314. flag = 0; // 用于控制本次逆转是否有效
  315. while(flag == 0)
  316. {
  317. pick1 = ((double)rand())/(RAND_MAX+1.0);
  318. pick2 = ((double)rand())/(RAND_MAX+1.0);
  319. pos1 = (int)(pick1*lenchrom); // 选取进行逆转操作的位置
  320. pos2 = (int)(pick2*lenchrom);
  321. while(pos1 > lenchrom-1)
  322. {
  323. pick1 = ((double)rand())/(RAND_MAX+1.0);
  324. pos1 = (int)(pick1*lenchrom);
  325. }
  326. while(pos2 > lenchrom -1)
  327. {
  328. pick2 = ((double)rand())/(RAND_MAX+1.0);
  329. pos2 = (int)(pick2*lenchrom);
  330. }
  331. if(pos1 > pos2)
  332. {
  333. temp = pos1;
  334. pos1 = pos2;
  335. pos2 = temp; // 交换使得pos1 <= pos2
  336. }
  337. if(pos1 < pos2)
  338. {
  339. for(int j=0;j<lenchrom;j++)
  340. reverse_arr[j] = chrom[i][j]; // 复制数组
  341. n = 0; // 逆转数目
  342. for(int j=pos1;j<=pos2;j++)
  343. {
  344. reverse_arr[j] = chrom[i][pos2-n]; // 逆转数组
  345. n++;
  346. }
  347. reverse_dis = path_len(reverse_arr); // 逆转之后的距离
  348. dis = path_len(chrom[i]); // 原始距离
  349. if(reverse_dis < dis)
  350. {
  351. for(int j=0;j<lenchrom;j++)
  352. chrom[i][j] = reverse_arr[j]; // 更新个体
  353. }
  354. }
  355. flag = 1;
  356. }
  357.  
  358. }
  359. }
  360.  
  361. // 主函数
  362. int main(void)
  363. {
  364. time_t start,finish;
  365. start = clock(); // 开始计时
  366. srand((unsigned)time(NULL)); // 初始化随机数种子
  367. init(); // 初始化种群
  368.  
  369. int best_fit_index = 0; //最短路径出现代数
  370. double distance_arr[sizepop];
  371. double dis;
  372. for(int j=0;j<sizepop;j++)
  373. {
  374. dis = path_len(chrom[j]);
  375. distance_arr[j] = dis;
  376. }
  377. double * best_index = min(distance_arr); // 计算最短路径及序号
  378. min_distance = *(best_index+1); // 最短路径
  379. int index = (int)(*best_index); // 最短路径序号
  380. for(int j=0;j<lenchrom;j++)
  381. best_result[j] = chrom[index][j]; // 最短路径序列
  382.  
  383. // 开始进化
  384. double * new_arr;
  385. double new_min_dis;
  386. int new_index;
  387. for(int i=0;i<maxgen;i++)
  388. {
  389. Choice(chrom); // 选择
  390. Cross(chrom); //交叉
  391. Mutation(chrom); //变异
  392. Reverse(chrom); // 逆转操作
  393. for(int j=0;j<sizepop;j++)
  394. distance_arr[j] = path_len(chrom[j]); // 距离数组
  395. new_arr = min(distance_arr);
  396. new_min_dis = *(new_arr+1); //新的最短路径
  397. if(new_min_dis < min_distance)
  398. {
  399. min_distance = new_min_dis; // 更新最短路径
  400. new_index =(int)(*new_arr);
  401. for(int j=0;j<lenchrom;j++)
  402. best_result[j] = chrom[new_index][j]; // 更新最短路径序列
  403. best_fit_index = i+1; // 最短路径代数
  404. }
  405. }
  406. finish = clock(); // 计算结束
  407. double duration = ((double)(finish-start))/CLOCKS_PER_SEC; // 计算耗时
  408. printf("本程序使用遗传算法求解规模为%d的TSP问题,种群数目为:%d,进化代数为:%d\n",lenchrom,sizepop,maxgen);
  409. printf("得到最短路径为:%d-->%d-->%d-->%d-->%d-->%d-->%d-->%d-->%d-->%d-->%d-->%d-->%d-->%d\n",best_result[0],best_result[1],best_result[2],
  410. best_result[3],best_result[4],best_result[5],best_result[6],best_result[7],best_result[8],best_result[9],best_result[10],best_result[11],
  411. best_result[12],best_result[13]);
  412. printf("最短路径长度为:%lf,得到最短路径在第%d代.\n",min_distance,best_fit_index);
  413. printf("程序耗时:%lf秒.\n",duration);
  414. return 0;
  415. }

这里取种群数目为100,最大进化代数为200,在ubuntu16.04下使用gcc编译器得到结果如下:

经过多次求解发现,在到了一定代数之后最优解保持不变,而且多次求解得到的最优路径相同,可以认为求得了问题的实际最优解。但是这里城市个数只有14个,属于规模较小的TSP问题,我决定再将问题规模变大来测试遗传算法优化的性能。这里选用规模为31的中国TSP问题,首先保持种群数目和进化代数不变,得到结果如下:

可以发现遗传算法得到的最短路径大概为16136.633514,而已知的中国TSP问题的最优解为15377,还是有一定的差距,并且算法有的时候收敛过早,陷入了局部最优解,并且稳定性较差,每次运行得到的结果波动较大。为了解决这个问题,我们决定将种群数目和进化代数增大,种群数目变为500,进化代数变为1000,重新运行程序得到的结果如下:

多次运行程序给出的最优解相近,波动很小,其中最好的一次为上图中的15380.515324,与真实最优解15377十分接近,这说明在增大种群规模以及进化代数之后遗传算法的优化能力又得到了进一步地提高,而且不易陷入局部最优解,总是能找到接近最优解的次优解,这也充分说明了遗传算法对于求解TSP问题还是十分有效的,也说明了遗传算法的普适性。

当然,遗传算法也有其局限性,比如对于大规模的寻优问题容易早熟,陷入局部最优等等,如果有机会我后面还会再补充这方面的内容,以及遗传算法在其他领域的应用。

遗传算法的C语言实现(二)的更多相关文章

  1. 遗传算法的C语言实现(二)-----以求解TSP问题为例

    上一次我们使用遗传算法求解了一个较为复杂的多元非线性函数的极值问题,也基本了解了遗传算法的实现基本步骤.这一次,我再以经典的TSP问题为例,更加深入地说明遗传算法中选择.交叉.变异等核心步骤的实现.而 ...

  2. 使用C语言实现二维,三维绘图算法(1)-透视投影

    使用C语言实现二维,三维绘图算法(1)-透视投影 ---- 引言---- 每次使用OpenGL或DirectX写三维程序的时候, 都有一种隔靴搔痒的感觉, 对于内部的三维算法的实现不甚了解. 其实想想 ...

  3. 使用C语言实现二维,三维绘图算法(3)-简单的二维分形

    使用C语言实现二维,三维绘图算法(3)-简单的二维分形 ---- 引言---- 每次使用OpenGL或DirectX写三维程序的时候, 都有一种隔靴搔痒的感觉, 对于内部的三维算法的实现不甚了解. 其 ...

  4. 使用C语言实现二维,三维绘图算法(2)-解析曲面的显示

    使用C语言实现二维,三维绘图算法(2)-解析曲面的显示 ---- 引言---- 每次使用OpenGL或DirectX写三维程序的时候, 都有一种隔靴搔痒的感觉, 对于内部的三维算法的实现不甚了解. 其 ...

  5. Swift语言指南(二)--语言基础之注释和分号

    原文:Swift语言指南(二)--语言基础之注释和分号 注释 通过注释向自己的代码中注入不可执行的文本,作为你自己的笔记或提示.Swift编译器运行时会忽略注释. Swift的注释与C语言极其相似,单 ...

  6. #r语言(二)笔记

    #r语言(二)笔记 #早复习 #概述:R是用于统计分析.绘图的语言和操作环境 #对象: #数据类型--统称为对象 #向量(vector):用于存储数值型.字符型或逻辑型数据的一维数组. #定义向量: ...

  7. iOS开发之SQLite-C语言接口规范(二) —— Prepared Your SQL Statements

    在<SQLite的C语言接口规范(一)>中介绍了如何去连接打开数据库,本篇博客就介绍如何操作数据库,本篇主要给出了如何执行数据库查询语句(Select), 然后遍历结果集.本篇博客就直接使 ...

  8. 遗传算法的C语言实现(一):以非线性函数求极值为例

    以前搞数学建模的时候,研究过(其实也不算是研究,只是大概了解)一些人工智能算法,比如前面已经说过的粒子群算法(PSO),还有著名的遗传算法(GA),模拟退火算法(SA),蚁群算法(ACA)等.当时懂得 ...

  9. C语言 预处理二(宏定义--#define)

    //#define 宏定义(宏定义一般大写) //知识点一-->#define的作用域:从#define开始,从上往下,如果遇到#undef就到#undef处结束,如果没有就是作用于当前整个文件 ...

随机推荐

  1. [VBA]合并工作簿优化版

    Sub 合并工作簿数据()Dim arrDim i As Integer, j As Integer, x As IntegerDim f As String, m As String, n As S ...

  2. python中_new_()与_init_()的区别

    __new__方法的使用 只有继承于object的新式类才能有__new__方法,__new__方法在创建类实例对象时由Python解释器自动调用,一般不用自己定义,Python默认调用该类的直接父类 ...

  3. linux(centOS7)的基本操作(三) 用户、组、权限管理

    用户和组 1.用户.组.家目录的概念 linux系统支持多用户,除了管理员,其他用户一般不应该使用root,而是应该向管理员申请一个账号.组类似于角色,系统可以通过组对有共性的用户进行统一管理.每个用 ...

  4. CompletableFuture引入

    一.Future介绍 Future以前我们如果有个方法action执行,比如去数据库中查询数据.上传一个文件等,这个方法执行10分钟,调用者就需要等10分钟.基于此,调用者可以先执行action,返回 ...

  5. 阶段3 2.Spring_08.面向切面编程 AOP_4 spring基于XML的AOP-配置步骤

    resources下新建bean.xml文件 xmlns:aop 先配置IOC aop 通知类就是logger.id配置为logAdvice表示日志的通知 梳理流程 首先我们在这有个Service它需 ...

  6. 阶段3 2.Spring_08.面向切面编程 AOP_2 spring中的aop术语和细节

    基于接口的动态代理要求,要去被代理对象最少实现一个接口 基于子类的动态代理没有实现接口的要求.但是要求我们的被代理类不能是最终类 在Spring中可以选择基于接口还是子类的动态代理 术语 大白话.业务 ...

  7. CSS未完

    CSS介绍 CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素. 当浏览器读到一个样式表,它就会按照这个样式表来对文档进行格式化(渲染). CSS语法 CSS实例 ...

  8. 剑指OFFER数据结构与算法分类

    目录 数据结构 算法 数据结构 数组 有序二维数组查找 数组相对位置排序 数组顺时针输出 把数组排成最小的数 数组中的逆序对 扑克牌顺子 数组中重复的数字 构建乘积数组 链表 链表反向插入ArrayL ...

  9. 应用安全 - 工具 - 中间件 - Apache - Apache Tika - 漏洞汇总

    CVE-2016-6809 Date2016 类型远程代码执行 影响范围Apache Tika 1.6-1.13 CVE-2018-1335 Date2018 类型命令注入 影响范围Tika-serv ...

  10. Mybatis--<![CDATA[ sql 语句 ]]>

    在mapper文件中写sql语句时,遇到特殊字符时,如:< 等,建议使用<![CDATA[ sql 语句 ]]>标记,将sql语句包裹住,不被解析器解析   在使用mybatis 时 ...