电子鱼

  • 首页
  • 文章
  • 公告
  • 站点地图
  • 友情链接
eleFish
计算机·硬件·图形学
  1. 首页
  2. 计算机
  3. 正文

五言绝句终极穷举器 C实现

2025年8月26日 69点热度 0人点赞 0条评论
// Ultimate Poem Forge — 五言绝句终极穷举器(C + UTF‑8)
// By Ziyang-Bai Copyright 2025 Ziyang-Bai allrights reserved!
// ---------------------------------------------------------------------------
// 说明
// 本程序按“Unicode 数值顺序”的汉字表进行枚举,生成所有可能的五言绝句
//(4 句 × 每句 5 字,共 20 字)。
//
// 警告:
//   默认仅使用 CJK Unified Ideographs(U+4E00–U+9FFF)与扩展 A
//   (U+3400–U+4DBF)。两个区合计 27,584 个汉字。全部 20 字位
//   的组合数约为 27,584^20,天文数量,任何机器都无法在有限时间内跑完。
//   程序提供“起始索引/计数/续跑/输出文件”等参数用于分片与实验。
//
// 你也可以通过“范围文件”添加更多扩展区(B、C、D、E、F、G、H…)。
// 范围文件格式:每行一个区间,十六进制起止码位,形如:
//   3400 4DBF
//   4E00 9FFF
// 行内用空白分隔,起止都包含(inclusive)。
//
// 枚举顺序:将所有可用码位按数值升序拼成一张“字表”,对 20 个字位做
// 基数为 |表| 的里程表递增(最右位先变)。输出 UTF‑8 文本:
//   每行 5 字,共 4 行;两首之间空一行。
//
// 构建:
//   gcc -O3 -std=c11 -Wall -Wextra -o poem_forge poem_forge.c
// 运行示例(仅试跑 3 首,起始索引 0,输出到文件):
//   ./poem_forge --count 3 --start 0 --out poems.txt
// 指定自定义范围文件(覆盖默认两个区):
//   ./poem_forge --ranges ranges.txt --count 2
// 追加输出:
//   ./poem_forge --start 1000000 --count 1000 --out part2.txt
//
// 参数:
//   --start <uint64>   起始组合索引(默认 0)
//   --count <uint64>   要生成的首数(默认无限,建议务必设置)
//   --out <path>       输出文件(默认 stdout)
//   --ranges <path>    使用范围文件(若未提供则用内置两个区)
//   --checkpoint <n>   每生成 n 首在 stderr 报告一次进度(默认 0=不报)
//
// ---------------------------------------------------------------------------

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <ctype.h>

#ifndef _WIN32
  #include <unistd.h>
#endif

// 一首诗 4 句 × 每句 5 字
#define LINES 4
#define CHARS_PER_LINE 5
#define POEM_LEN (LINES * CHARS_PER_LINE)

typedef struct { uint32_t start, end; } Range; // inclusive

typedef struct {
    uint32_t *table;      // 升序的码位表
    size_t    size;       // 表长
} CodeTable;

static int cmp_u32(const void *a, const void *b) {
    uint32_t ua = *(const uint32_t*)a;
    uint32_t ub = *(const uint32_t*)b;
    if (ua < ub) return -1;
    if (ua > ub) return 1;
    return 0;
}

static int hex32(const char *s, uint32_t *out) {
    // 解析十六进制(不需要 0x 前缀)
    char *end = NULL;
    unsigned long v = strtoul(s, &end, 16);
    if (end == s || *end != '\0' || v > 0x10FFFFUL) return -1;
    *out = (uint32_t)v;
    return 0;
}

static int load_ranges_from_file(const char *path, Range **ranges_out, size_t *n_out) {
    FILE *f = fopen(path, "r");
    if (!f) return -1;
    size_t cap = 16, n = 0; 
    Range *arr = (Range*)malloc(cap * sizeof(Range));
    if (!arr) { fclose(f); return -1; }
    char line[256];
    while (fgets(line, sizeof(line), f)) {
        // 跳过注释与空行
        char *p = line; while (isspace((unsigned char)*p)) ++p;
        if (*p == '\0' || *p == '#') continue;
        char a[64], b[64];
        if (sscanf(p, "%63s %63s", a, b) != 2) continue;
        uint32_t s, e; if (hex32(a, &s) || hex32(b, &e)) continue;
        if (s > e) { uint32_t t=s; s=e; e=t; }
        if (n == cap) { cap *= 2; arr = (Range*)realloc(arr, cap*sizeof(Range)); if (!arr){ fclose(f); return -1; } }
        arr[n++] = (Range){ s, e };
    }
    fclose(f);
    *ranges_out = arr; *n_out = n; return 0;
}

static CodeTable build_table_from_ranges(const Range *ranges, size_t n) {
    // 统计总数
    uint64_t total = 0;
    for (size_t i=0;i<n;i++) {
        if (ranges[i].start > ranges[i].end) continue;
        total += (uint64_t)ranges[i].end - (uint64_t)ranges[i].start + 1ULL;
    }
    if (total == 0) return (CodeTable){NULL,0};
    if (total > SIZE_MAX/sizeof(uint32_t)) {
        fprintf(stderr, "[fatal] 字表过大,无法分配内存\n");
        return (CodeTable){NULL,0};
    }
    uint32_t *table = (uint32_t*)malloc((size_t)total * sizeof(uint32_t));
    if (!table) return (CodeTable){NULL,0};
    size_t k=0;
    for (size_t i=0;i<n;i++) {
        for (uint32_t cp = ranges[i].start; cp <= ranges[i].end; ++cp) {
            table[k++] = cp;
            if (cp == 0x10FFFF) break; // 防溢出保护
        }
    }
    // 排序并去重(范围可能重叠)
    qsort(table, k, sizeof(uint32_t), cmp_u32);
    size_t u = 0; 
    for (size_t i=0;i<k;i++) {
        if (i==0 || table[i]!=table[i-1]) table[u++] = table[i];
    }
    return (CodeTable){ table, u };
}

static CodeTable default_table(void) {
    // 默认仅含:扩展 A(3400–4DBF)与基本区(4E00–9FFF)
    Range r[] = {
        {0x3400, 0x4DBF},
        {0x4E00, 0x9FFF},
    };
    return build_table_from_ranges(r, sizeof(r)/sizeof(r[0]));
}

// 将 Unicode 码位编码为 UTF‑8,返回字节数(1–4),失败返回 0
static int utf8_encode(uint32_t cp, unsigned char out[4]) {
    if (cp <= 0x7F) { out[0] = (unsigned char)cp; return 1; }
    else if (cp <= 0x7FF) { out[0] = 0xC0 | (cp >> 6); out[1] = 0x80 | (cp & 0x3F); return 2; }
    else if (cp <= 0xFFFF) {
        // 排除 UTF‑16 代理项(不是有效字符)
        if (cp >= 0xD800 && cp <= 0xDFFF) return 0;
        out[0] = 0xE0 | (cp >> 12);
        out[1] = 0x80 | ((cp >> 6) & 0x3F);
        out[2] = 0x80 | (cp & 0x3F);
        return 3;
    } else if (cp <= 0x10FFFF) {
        out[0] = 0xF0 | (cp >> 18);
        out[1] = 0x80 | ((cp >> 12) & 0x3F);
        out[2] = 0x80 | ((cp >> 6) & 0x3F);
        out[3] = 0x80 | (cp & 0x3F);
        return 4;
    }
    return 0;
}

// 写出一个码位到文件
static int write_cp_utf8(FILE *fp, uint32_t cp) {
    unsigned char buf[4];
    int n = utf8_encode(cp, buf);
    if (n <= 0) return -1;
    return (fwrite(buf, 1, (size_t)n, fp) == (size_t)n) ? 0 : -1;
}

// 迭代器:将“全局组合索引”映射到 20 位“数字”(基数为表长)
static void index_to_digits(uint64_t idx, size_t base, size_t digits[POEM_LEN]) {
    for (int i = POEM_LEN - 1; i >= 0; --i) {
        digits[i] = (size_t)(idx % base);
        idx /= base;
    }
}

static int increment_digits(size_t digits[POEM_LEN], size_t base) {
    for (int i = POEM_LEN - 1; i >= 0; --i) {
        if (++digits[i] < base) return 1; // 成功加一
        digits[i] = 0;
    }
    return 0; // 溢出,已到尽头
}

static void usage(const char *argv0) {
    fprintf(stderr,
        "用法: %s [--start N] [--count M] [--out FILE] [--ranges FILE] [--checkpoint K]\n"
        "  --start N      起始组合索引(默认 0)\n"
        "  --count M      生成 M 首(默认直到穷尽;强烈建议设置)\n"
        "  --out FILE     输出文件(默认 stdout)\n"
        "  --ranges FILE  从文件加载码位区间(若不给则使用内置 A+基本区)\n"
        "  --checkpoint K 每生成 K 首在 stderr 报告一次进度\n",
        argv0);
}

int main(int argc, char **argv) {
    uint64_t start = 0;
    uint64_t count = UINT64_MAX; // 不限
    const char *outpath = NULL;
    const char *ranges_path = NULL;
    uint64_t checkpoint = 0;

    for (int i=1;i<argc;i++) {
        if (!strcmp(argv[i], "--start") && i+1<argc) { start = strtoull(argv[++i], NULL, 10); }
        else if (!strcmp(argv[i], "--count") && i+1<argc) { count = strtoull(argv[++i], NULL, 10); }
        else if (!strcmp(argv[i], "--out") && i+1<argc) { outpath = argv[++i]; }
        else if (!strcmp(argv[i], "--ranges") && i+1<argc) { ranges_path = argv[++i]; }
        else if (!strcmp(argv[i], "--checkpoint") && i+1<argc) { checkpoint = strtoull(argv[++i], NULL, 10); }
        else { usage(argv[0]); return 1; }
    }

    Range *ranges = NULL; size_t n_ranges = 0; CodeTable tab = {0};
    if (ranges_path) {
        if (load_ranges_from_file(ranges_path, &ranges, &n_ranges) != 0 || n_ranges == 0) {
            fprintf(stderr, "[error] 无法读取范围文件或为空:%s\n", ranges_path);
            return 1;
        }
        tab = build_table_from_ranges(ranges, n_ranges);
    } else {
        tab = default_table();
    }
    if (!tab.table || tab.size == 0) {
        fprintf(stderr, "[fatal] 字表为空\n");
        return 1;
    }

    FILE *out = stdout;
    if (outpath) {
        out = fopen(outpath, "wb");
        if (!out) {
            fprintf(stderr, "[error] 打开输出文件失败:%s (%s)\n", outpath, strerror(errno));
            free(tab.table); free(ranges); return 1;
        }
    }

    // 提高 I/O 吞吐
    setvbuf(out, NULL, _IOFBF, 1<<20); // 1 MiB 缓冲

    // 初始化“里程表”
    size_t base = tab.size;
    size_t digits[POEM_LEN];
    index_to_digits(start, base, digits);

    uint64_t produced = 0;
    uint64_t idx = start;

    while (produced < count) {
        // 输出一首:4 行 × 每行 5 字
        for (int line = 0; line < LINES; ++line) {
            for (int c = 0; c < CHARS_PER_LINE; ++c) {
                size_t pos = (size_t)line * CHARS_PER_LINE + (size_t)c;
                uint32_t cp = tab.table[ digits[pos] ];
                if (write_cp_utf8(out, cp) != 0) { fprintf(stderr, "[error] 写入失败\n"); goto done; }
            }
            fputc('\n', out);
        }
        fputc('\n', out);
        produced++; idx++;

        if (checkpoint && (produced % checkpoint == 0)) {
            fprintf(stderr, "[progress] start=%" PRIu64 ", emitted=%" PRIu64 " (idx=%" PRIu64 ")\n", start, produced, idx);
        }

        if (!increment_digits(digits, base)) {
            break; // 穷尽
        }
    }

 done:
    if (out && out != stdout) fclose(out);
    free(tab.table);
    free(ranges);

    if (produced < count) {
        fprintf(stderr, "
已到字表的末尾或提前终止。总输出 %" PRIu64 " 首。\n", produced); } else { fprintf(stderr, "
正常结束。总输出 %" PRIu64 " 首,最后索引 %" PRIu64 "。\n", produced, idx-1); } return 0; }

 

标签: 暂无
最后更新:2025年8月26日

Ziyang-Bai

这个人很懒,什么都没留下

点赞
< 上一篇

文章评论

您需要 登录 之后才可以评论
no pic now

wapmz.com">

版权所有 © 2025 电子鱼. 保留所有权利

Theme Kratos Made By Seaton Jiang

浙ICP备2024108866号-2