找回密码
 立即注册
首页 业界区 安全 0.02秒找到最优解的华容道程序

0.02秒找到最优解的华容道程序

擒揭 昨天 14:00
 
1.png
 
  1. #include <stdio.h>
  2. #include <stdint.h>
  3. #include <string.h>
  4. #include <time.h>
  5. #include <immintrin.h>
  6. #include <xmmintrin.h>
  7. #include <set>
  8. #include "cwisstable.h"
  9. const char* NM[][4] = { {"曹","贼","真","爽"}, {"西","施"}, {"昭","君"}, {"貂","蝉"}, {"甄","姬"}, {"玉","环"}, {"美"}, {"美"}, {"美"}, {"美"} };
  10. int W[] = { 2, 2, 2, 2, 2, 2, 1, 1, 1, 1 }; // 默认5个水平条,随后修改
  11. int H[] = { 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
  12. int T[10]; // Type
  13. const int D[][2] = { {0, 1}, {0, -1}, {-1, 0}, {1, 0} }; // 后面有两处用下标判断移动方向
  14. enum { MAX = 3600 * 10000 };
  15. struct State {
  16.   uint8_t aa[10][2];
  17. #define CCY aa[0][0] // 曹操的y
  18.   int p; // 局面路径的previous
  19. #define cpy20(dst, src) _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((__m128i*)src)); *(int*)((uint8_t*)dst+16) = *(int*)((const uint8_t*)src+16)
  20.   void operator= (const State& s) { cpy20(aa, s.aa); p = s.p; }
  21.   void operator=(const char* s);
  22.   void print(const char* s = "");
  23. } q[MAX + 10 * 4]; // 最多40个move. MAX很大; 没判断qt < MAX
  24. int qh, qt = 1; // queue head, tail
  25. void State::operator= (const char* s) {
  26.   int p = 6;
  27.   for (int x = 3; x >= 0; x--)
  28.     for (int y = 4; y >= 0; y--) {
  29.     #define CASE(c, i) case c: aa[i][1] = x; aa[i][0] = y; break;
  30.     switch (s[x * 5 + y]) {
  31.     // 曹关张黄子龙(l)马
  32.     CASE('c', 0) CASE('g', 1) CASE('z', 2) CASE('h', 3) CASE('l', 4) CASE('m', 5)
  33.     case 'p':
  34.       aa[p][1] = x; aa[p++][0] = y; break; // pawn
  35.     }
  36.   }
  37.   static const char*  S[] = { "gg", "zz", "hh", "ll", "mm" };
  38.   for (int i = 0; i < 5; i++) if (strstr(s, S[i])) W[i+1] = 1, H[i+1] = 2;
  39.   for (int i = 0; i < 10; i++) T[i] = W[i] * 2 + H[i] - 2;
  40. }
  41. #define set2DArrayByCoordInA(a, b, what) for (int i = 0; i < 10; i++) { \
  42.   const int x = a[i][1], y = a[i][0]; \
  43.   for (int yy = y; yy < y + H[i]; yy++) \
  44.     for (int xx = x; xx < x + W[i]; xx++) b[yy][xx] = what; \
  45. }
  46. void State::print (const char* s) {
  47.   int idx[10] = {};
  48.   const char* b[5][4] = {};
  49.   set2DArrayByCoordInA(aa, b, NM[i][idx[i]++])
  50.   for (int y = 0; y < 5; y++) {
  51.     for (int x = 0; x < 4; x++) printf("%s", b[y][x] ? : " ");
  52.     puts("");
  53.   }
  54.   printf("%s\n", s);
  55. }
  56. void print_path () { // 数组存的单链表就地翻转
  57.   int prev = -1, next = -1, n = 0;
  58.   for (int cur = qh; cur != -1; ++n) {
  59.     next = q[cur].p; q[cur].p = prev;
  60.     prev = cur; cur = next;
  61.   }
  62.   for (int p = 0; p != -1; p = q[p].p) q[p].print();
  63.   printf("%d\n", n);
  64. }
  65. #pragma pack(1)
  66. struct {
  67.   uint8_t b[5][4];
  68.   uint64_t n; // unique number
  69.   uint8_t _[4];
  70. } u __attribute__((aligned(32)));
  71. #pragma pack()
  72. #define Ah { \
  73.   _mm256_store_si256((__m256i*)&u, _mm256_setzero_si256()); \
  74.   set2DArrayByCoordInA(a, u.b, T[i]) \
  75.   for (int y = 0; y < 5; y++) \
  76.     for (int x = 0; x < 4; x++) u.n = u.n * 5 + u.b[y][x]; \
  77. }
  78. #ifdef st
  79. CWISS_DECLARE_FLAT_HASHSET(Set, uint64_t); Set set = Set_new(MAX);
  80. #define if_ok_add_state if (ok) { cpy20(q[qt].aa, a); \
  81.   Ah if (!Set_contains(&set, &u.n)) { Set_insert(&set, &u.n); q[qt++].p = qh; } \
  82. }
  83. #else
  84. std::set<uint64_t> set;
  85. #define if_ok_add_state if (ok) { cpy20(q[qt].aa, a); \
  86.   Ah if (set.find(u.n) == set.end()) { set.insert(u.n); q[qt++].p = qh; } \
  87. }
  88. #endif
  89. int main (int argc, char* argv[]) {
  90.   //q[0] = "pp zz""ccghh""ccgll""pp mm";
  91.   q[0] = (argc == 2) ? argv[1] : "pzzpg""cc  g""ccllm""phhpm";
  92.   q[0].p = -1;
  93.   uint8_t a[10][2] __attribute__((aligned(32)));
  94.   cpy20(a, q[0].aa);
  95. #ifdef st
  96.   Ah Set_insert(&set, &u.n);
  97. #else
  98.   Ah set.insert(u.n);
  99. #endif
  100.   double tm = clock();
  101.   for (; qh < qt; qh++) {
  102.     if (q[qh].CCY == 3) { print_path(); break; }
  103.     cpy20(a, q[qh].aa);
  104.     uint8_t b[5][4] __attribute__((aligned(32))), overflow[12];
  105.     _mm256_store_si256((__m256i*)b, _mm256_set1_epi8(1));
  106.     set2DArrayByCoordInA(a, b, 0)
  107.     int qt1 = qt;
  108.     for (int j = 0; j < 4; j++) {
  109.       int ok;
  110.       for (int i = 0; i < 6; i++) {
  111.         const int ox = a[i][1], oy = a[i][0];
  112.         int x = (a[i][1] += D[j][0]), y = (a[i][0] += D[j][1]);
  113.         if (x < 0 || x + W[i] > 4 || y < 0 || y + H[i] > 5) ok = 0;
  114.         else { // D[]的顺序不能换!
  115.           if (j == 0) { y = oy + H[i]; ok = b[y][x] && (W[i] == 1 || b[y][x + 1]); }
  116.           else if (j == 1) ok = b[y][x] & (!(W[i] - 1) | b[y][x + 1]); // W[i]1或2; 不更快
  117.           else if (j == 2) ok = b[y][x] & (!(H[i] - 1) | b[y + 1][x]);
  118.           else { x = ox + W[i]; ok = b[y][x] && (H[i] == 1 || b[y + 1][x]); }
  119.         }
  120.         if_ok_add_state
  121.         a[i][1] -= D[j][0]; a[i][0] -= D[j][1];
  122.       }
  123.       for (int i = 6; i < 10; i++) {
  124.         const int ox = a[i][1], oy = a[i][0];
  125.         int x = (a[i][1] += D[j][0]), y = (a[i][0] += D[j][1]);
  126.         if (x < 0 || x + W[i] > 4 || y < 0 || y + H[i] > 5) ok = 0;
  127.         else {
  128.           if (j == 0) y = oy + H[i];
  129.           else if (j == 3) x = ox + W[i];
  130.           ok = b[y][x];
  131.         }
  132.         if_ok_add_state
  133.         a[i][1] -= D[j][0]; a[i][0] -= D[j][1];
  134.       }
  135.     }
  136. #if 1
  137.     int qt2 = qt - 1;
  138.     if (qt2 > qt1 && q[qt2].CCY < q[qt1].CCY) { State t = q[qt1]; q[qt1] = q[qt2]; q[qt2] = t; }
  139. #endif
  140.   }
  141.   printf("%.6f %d\n", (clock() - tm) / CLOCKS_PER_SEC, qt);
  142.   return 0;
  143. }
复制代码
有个负优化而不是bug. q[qt2].CCY < q[qt1].CCY的目的是优先搜索曹操靠下的局面。下方的y大,< 应为 >
既然优先把所有的块往下挪,为啥会出现q[qt2].CCY < q[qt1].CCY的情形呢?
当前局面是最左边时,程序确实优先往下挪,可挪不动。有2个合法移动:
美  美  美曹贼美  美 美
美曹贼貂  美真爽貂  美曹贼貂
昭真爽蝉  昭  蝉  昭真爽蝉
君甄玉环  君甄玉环  君甄玉环
美姬西施  美姬西施  美姬西施
我们是方向优先,且优先往下(前提是能挪动),块则是曹操第一,往下挪不动往上挪了。
qt1=11788 qt2=11789 qt=11790 (尚未使用)
把,和#if 0后发现,这个启发信息没用,速度一样(起码差别微乎其微)。
这个程序速度很快。以上结果是在Intel N100上跑的。注释掉找到局面则退出的代码,0.081639秒内搜索了81,340个局面,判断出无解。
On a SUN Sparcstation 2, the timings increase (at a rate of 0.005 second/pixel) from about 20 seconds for a 64**2 pixel image to about 320 seconds for 256**2 pixels.详情
它功能强,支持各种块形状。我觉得华容道太难了,并不手动玩,也许可以改成3个长条6个小方块之类。
我alias gcc='g++ -mavx2'。Ah(q[0].aa)美吗?还是改成Emm?
“有2个合法移动”——不对啊,待查,结果:qh=10933 反正我0.02秒,不SEGV,不管啦!:-)
优化历史
AI写的五进制转换程序:
2.gif
3.gif
  1. #include <stdio.h>
  2. #include <stdint.h>
  3. #include <immintrin.h>
  4. // SSE优化版本
  5. uint64_t convert_to_quinary_sse(uint8_t b[5][4])
  6. {
  7.   uint64_t n = 0;
  8.   // 处理前16个元素(4行)使用SSE
  9.   for (int y = 0; y < 4; y++) {
  10.     // 加载4个uint8_t值到128位寄存器
  11.     __m128i row = _mm_loadu_si128((__m128i *) b[y]);
  12.     // 将uint8_t转换为uint64_t并进行五进制转换
  13.     for (int x = 0; x < 4; x++) {
  14.       uint8_t value = b[y][x];
  15.       n = n * 5 + value;
  16.     }
  17.   }
  18.   // 处理最后一行(第5行)
  19.   for (int x = 0; x < 4; x++) {
  20.     n = n * 5 + b[4][x];
  21.   }
  22.   return n;
  23. }
  24. // AVX2优化版本(处理所有20个元素)
  25. uint64_t convert_to_quinary_avx2(uint8_t b[5][4])
  26. {
  27.   uint64_t n = 0;
  28.   // 使用AVX2处理前16个元素
  29.   for (int y = 0; y < 4; y++) {
  30.     // 加载4个uint8_t值
  31.     __m128i row = _mm_loadu_si128((__m128i *) b[y]);
  32.     // 解包到32位整数
  33.     __m128i zero = _mm_setzero_si128();
  34.     __m128i row32 = _mm_cvtepu8_epi32(row);
  35.     // 对每个元素进行五进制转换
  36.     uint32_t values[4];
  37.     _mm_storeu_si128((__m128i *) values, row32);
  38.     for (int i = 0; i < 4; i++) {
  39.       n = n * 5 + values[i];
  40.     }
  41.   }
  42.   // 处理最后4个元素
  43.   for (int x = 0; x < 4; x++) {
  44.     n = n * 5 + b[4][x];
  45.   }
  46.   return n;
  47. }
  48. // 高度优化的AVX版本,使用并行计算
  49. uint64_t convert_to_quinary_avx_parallel(uint8_t b[5][4])
  50. {
  51.   // 使用多个累加器并行计算
  52.   uint64_t n1 = 0, n2 = 0, n3 = 0, n4 = 0;
  53.   // 并行处理4行
  54.   for (int y = 0; y < 4; y++) {
  55.     // 加载当前行
  56.     __m128i row = _mm_loadu_si128((__m128i *) b[y]);
  57.     // 转换为32位整数
  58.     __m128i row32 = _mm_cvtepu8_epi32(row);
  59.     uint32_t values[4];
  60.     _mm_storeu_si128((__m128i *) values, row32);
  61.     // 并行累加(模拟并行处理)
  62.     n1 = n1 * 625 + values[0] * 125 + values[1] * 25 + values[2] * 5 + values[3];
  63.   }
  64.   // 合并结果并添加最后一行
  65.   uint64_t final_n = n1;
  66.   for (int x = 0; x < 4; x++) {
  67.     final_n = final_n * 5 + b[4][x];
  68.   }
  69.   return final_n;
  70. }
  71. // 基准版本(原始代码)
  72. uint64_t convert_to_quinary_baseline(uint8_t b[5][4])
  73. {
  74.   uint64_t n = 0;
  75.   for (int y = 0; y < 5; y++) {
  76.     for (int x = 0; x < 4; x++) {
  77.       n = n * 5 + b[y][x];
  78.     }
  79.   }
  80.   return n;
  81. }
  82. int main()
  83. {
  84.   uint8_t b[5][4] = {
  85.     { 1, 2, 3, 4 },
  86.     { 0, 4, 2, 1 },
  87.     { 3, 1, 4, 2 },
  88.     { 2, 0, 1, 3 },
  89.     { 4, 3, 0, 2 }
  90.   };
  91.   uint64_t result_baseline = convert_to_quinary_baseline(b);
  92.   uint64_t result_sse = convert_to_quinary_sse(b);
  93.   uint64_t result_avx2 = convert_to_quinary_avx2(b);
  94.   uint64_t result_avx_parallel = convert_to_quinary_avx_parallel(b);
  95.   printf("基准版本结果: %lu\n", result_baseline);
  96.   printf("SSE优化结果:  %lu\n", result_sse);
  97.   printf("AVX2优化结果: %lu\n", result_avx2);
  98.   printf("AVX并行结果: %lu\n", result_avx_parallel);
  99.   if (result_baseline == result_sse &&
  100.       result_sse == result_avx2 && result_avx2 == result_avx_parallel) {
  101.     printf("✓ 所有版本结果一致\n");
  102.   } else
  103.     printf("✗ 结果不一致\n");
  104.   return 0;
  105. }
复制代码
View Code  

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册