2501_90878632 2026-05-07 20:17 采纳率: 20%
浏览 3

help!这个代码应该怎样优化??ncjesfnjckndsn

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define NHASH 1048576//定义哈希表长度

typedef struct HashTable
{
    char *w1,*w2;//前缀的第一个和第二个词
    char **suffix;//后缀列表,存储当前(w1,w2)前缀对应的所有后续词
    int n;//后缀数量,记录当前前缀下有多少个不同/重复的后缀
    unsigned int conflict;//冲突标识,基于w2计算的哈希值,用于快速区分不同前缀
    struct HashTable *next;
} HashTable;

double seed=997;//随机数种子
char *list[1200000];//存储所有分词结果
char *end="(end)";//文件结束标记
HashTable *Hash[NHASH];//哈希表数组

//生成随机数
double rrand()
{
    double lambda=3125.0;
    double m=34359738337.0;
    double r;
    seed=fmod(lambda*seed,m);
    r=seed/m;
    return r;
}

//主哈希函数,计算哈希地址
unsigned int HashFirst(char *w1,char *w2)
{
    unsigned int hash=0;

    //遍历w1的每个字符
    while(*w1)
        hash=131*hash+(*w1++);//哈希=131*旧哈希+当前字符ASCII码

    //遍历w2的每个字符
    while(*w2)
        hash=131*hash+*w2++;
    return hash%NHASH;//哈希值取模,得到哈希表地址
}

//冲突哈希函数,计算冲突校验值
unsigned int HashConflict(char *w2)
{
    unsigned int hash=5381;

    //遍历w2的每个字符,计算冲突校验值
    while(*w2)
        hash+=(hash*32)+(*w2++);
    return hash%NHASH;//取模得到冲突标识
}

//哈希表查找函数
HashTable *HashSearch(char *w1,char *w2)
{
    unsigned int addr=HashFirst(w1,w2);
    unsigned int conflict=HashConflict(w2);
    HashTable *p=Hash[addr];//指向当前第一个节点

    //遍历冲突链表查找匹配节点
    while(p!=NULL)
    {
        //校验冲突值,w1是否匹配,找到则返回节点地址
        if(conflict==p->conflict && !strcmp(p->w1,w1))
            return p;
        p=p->next;
    }
    return NULL;
}

//哈希表插入函数
void InsertHash(char *w1,char *w2,char *w3)
{
    unsigned int addr=HashFirst(w1,w2);
    unsigned int conflict=HashConflict(w2);

    if(!Hash[addr])
    {
        Hash[addr]=(HashTable *)malloc(sizeof(HashTable));
        Hash[addr]->w1=w1;//存储第一个前缀词
        Hash[addr]->w2=w2;//存储第二个前缀词
        Hash[addr]->next=NULL;
        Hash[addr]->n=1;
        Hash[addr]->conflict=conflict;
        Hash[addr]->suffix=(char **)malloc(sizeof(char *));
        Hash[addr]->suffix[0]=w3;//存储第一个后缀词
    }
    else//哈希地址已有节点,处理冲突
    {
        HashTable *p,*q;//p遍历链表,q记录p的前驱节点
        p=q=Hash[addr];//p和q初始指向链表头

        //遍历冲突链表
        while(1)
        {
            if(!p)//已经到达链表末尾但未找到匹配
            {
                HashTable *s=(HashTable *)malloc(sizeof(HashTable));
                s->w1=w1;
                s->w2=w2;
                s->next=NULL;
                s->n=1;
                s->conflict=conflict;
                s->suffix=(char **)malloc(sizeof(char *));//分配后缀数组空间
                s->suffix[0]=w3;
                q->next=s;
                return;
            }
            else if(!strcmp(p->w1,w1)&&!strcmp(p->w2,w2))//找到匹配的前缀节点
            {
                p->n++;
                p->suffix=(char **)realloc(p->suffix,p->n*sizeof(char *));
                p->suffix[p->n-1]=w3;
                return;
            }
            q=p;//记录当前节点为前驱
            p=p->next;
        }
    }
}

//生成文本并写入文件
void Write(int n,int maxLen)
{
    FILE *fw=fopen("markov.txt","w");
    char *w1,*w2,*w3;

    w1=list[0];
    w2=list[1];
    fprintf(fw,"%s %s ",w1,w2);
    maxLen-=2;

    //循环随机选择w3,从以原有的w1,w2为前缀的后缀中选一个
    while(maxLen--)//循环生成文本,直到达到最大长度
    {
        HashTable *p=HashSearch(w1,w2);//查找当前前缀对应的节点
        //随机选择后缀
        if(p->n==1)
            n=0;//只有一个后缀时直接选
        else
            n=(int)(rrand()*p->n);//多个后缀时随机选择
        w3=p->suffix[n];//选择的后缀
        //输出w3
        if(w3==end)//遇到结束标记
            break;
        fprintf(fw,"%s ",w3);
        //更新前缀:w1=w2,w2=w3
        w1=w2;
        w2=w3;
    }
    fclose(fw);
}

int main()
{
    FILE *fr=fopen("article.txt","r");
    int len=0,n=0,maxLen,i,j;
    char *book;//存储文件内容
    char buf[105];//临时存储单词
    fseek(fr,0,SEEK_END);//文件指针移到末尾
    len=ftell(fr);
    fseek(fr,0,SEEK_SET);//文件指针回到开头
    book=(char *)malloc(len+1);
    len=fread(book,1,len,fr);
    book[len]='\0';

    //分词:遍历文件内容提取单词
    for(i=0,j=0; i<len; i++)
    {
        //判断是否为可打印字符
        if(book[i]>32&&book[i]<127)
        {
            j=0;
            //连续读取字符组成单词
            while(i<len&&book[i]>32&&book[i]<127)
                buf[j++]=book[i++];
            buf[j]='\0';
            list[n]=malloc(strlen(buf)+1);//为单词分配空间
            strcpy(list[n],buf);//复制单词到数组
            n++;//单词数加1
        }
    }
    list[n]=end;//添加结束标记
    n++;
    fclose(fr);
    free(book);
    scanf("%d",&maxLen);

    //构建马尔可夫链哈希表
    for(i=0; i<n-2; i++)
        InsertHash(list[i],list[i+1],list[i+2]);//插入前缀和后缀
    Write(n,maxLen);//生成并写入文本
    return 0;
}
  • 写回答

1条回答 默认 最新

  • 檀越@新空间 2026-05-07 20:18
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    你提供的代码是一个基于马尔可夫链的文本生成程序,主要功能是根据输入文本构建一个哈希表,用于存储前缀(两个词)和其对应的后缀(多个词),然后通过随机选择后缀来生成新的文本。然而,这段代码存在一些性能问题、逻辑错误、内存管理不当以及潜在的崩溃风险


    一、当前代码存在的问题

    1. 代码不完整main() 函数中的分词部分被截断,无法运行。
    2. 内存泄漏:未释放 list 数组和哈希表中分配的内存。
    3. 哈希冲突处理不完善HashConflict 函数可能无法正确区分不同前缀。
    4. 字符串比较逻辑错误:在 HashSearchInsertHash 中,strcmp(p->w1, w1) 可能导致空指针异常。
    5. 哈希表插入逻辑错误:在 InsertHash 中,当找到匹配节点时,p->n++ 后没有重新分配 suffix 的大小。
    6. 随机数生成不规范rrand() 使用了固定值 lambda=3125.0m=34359738337.0,可能导致重复或不可预测的结果。
    7. 未处理文件读取失败:未检查 fopen 是否成功。
    8. 未处理 end 标记:在 Write() 函数中,若 w3 == end,但未处理后续逻辑。
    9. 未初始化 list 数组list[1200000] 未初始化,可能导致未定义行为。

    二、优化建议与解决方案

    1. 修复代码完整性

    确保所有函数完整,尤其是 main() 函数的分词部分。

    2. 优化内存管理

    • 在程序结束时,释放所有动态分配的内存。
    • 避免使用 mallocrealloc 直接操作原始字符指针,应使用 strcpystrdup

    3. 修复哈希冲突处理

    • HashConflict 应只基于 w2 计算,避免混淆 w1w2
    • 增加冲突校验逻辑,确保不同前缀不会误判为相同。

    4. 修复字符串比较逻辑

    • 确保 w1w2 不为 NULL,再进行 strcmp 操作。

    5. 修复哈希表插入逻辑

    • p->n++ 时,必须重新分配 suffix 的空间,否则会导致数组越界。

    6. 改进随机数生成

    • 使用更稳定的随机数生成方法,如 C 标准库中的 rand()srand()

    7. 增加错误处理

    • 检查文件打开是否成功,避免运行时崩溃。
    • 添加对 end 标记的处理逻辑。

    三、修改后的代码示例

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    
    #define NHASH 1048576 // 哈希表长度
    
    typedef struct HashTable {
        char *w1, *w2;           // 前缀的第一个和第二个词
        char **suffix;            // 后缀列表
        int n;                  // 后缀数量
        unsigned int conflict;  // 冲突标识
        struct HashTable *next;
    } HashTable;
    
    // 随机数生成器
    double rrand() {
        static double seed = 997;
        double lambda = 3125.0;
        double m = 34359738337.0;
        seed = fmod(lambda * seed, m);
        return seed / m;
    }
    
    // 主哈希函数
    unsigned int HashFirst(char *w1, char *w2) {
        unsigned int hash = 0;
        while (*w1) hash = 131 * hash + (*w1++);
        while (*w2) hash = 131 * hash + (*w2++);
        return hash % NHASH;
    }
    
    // 冲突哈希函数
    unsigned int HashConflict(char *w2) {
        unsigned int hash = 5381;
        while (*w2) hash = (hash << 5) + hash + (*w2++);
        return hash % NHASH;
    }
    
    // 哈希表查找
    HashTable *HashSearch(char *w1, char *w2) {
        if (!w1 || !w2) return NULL;
        unsigned int addr = HashFirst(w1, w2);
        unsigned int conflict = HashConflict(w2);
        HashTable *p = Hash[addr];
        while (p != NULL) {
            if (conflict == p->conflict && !strcmp(p->w1, w1))
                return p;
            p = p->next;
        }
        return NULL;
    }
    
    // 哈希表插入
    void InsertHash(char *w1, char *w2, char *w3) {
        if (!w1 || !w2 || !w3) return;
        unsigned int addr = HashFirst(w1, w2);
        unsigned int conflict = HashConflict(w2);
    
        HashTable *p = Hash[addr];
        if (!p) {
            Hash[addr] = (HashTable *)malloc(sizeof(HashTable));
            Hash[addr]->w1 = strdup(w1);
            Hash[addr]->w2 = strdup(w2);
            Hash[addr]->next = NULL;
            Hash[addr]->n = 1;
            Hash[addr]->conflict = conflict;
            Hash[addr]->suffix = (char **)malloc(sizeof(char *));
            Hash[addr]->suffix[0] = strdup(w3);
        } else {
            HashTable *q = Hash[addr];
            while (q != NULL) {
                if (!strcmp(q->w1, w1) && !strcmp(q->w2, w2)) {
                    q->n++;
                    q->suffix = (char **)realloc(q->suffix, q->n * sizeof(char *));
                    q->suffix[q->n - 1] = strdup(w3);
                    return;
                }
                q = q->next;
            }
    
            // 如果没有找到,则添加新节点
            HashTable *newNode = (HashTable *)malloc(sizeof(HashTable));
            newNode->w1 = strdup(w1);
            newNode->w2 = strdup(w2);
            newNode->next = NULL;
            newNode->n = 1;
            newNode->conflict = conflict;
            newNode->suffix = (char **)malloc(sizeof(char *));
            newNode->suffix[0] = strdup(w3);
            q->next = newNode;
        }
    }
    
    // 生成文本并写入文件
    void Write(int maxLen) {
        FILE *fw = fopen("markov.txt", "w");
        if (!fw) {
            fprintf(stderr, "Failed to open file.\n");
            return;
        }
    
        char *w1 = list[0], *w2 = list[1];
        fprintf(fw, "%s %s ", w1, w2);
        maxLen -= 2;
    
        while (maxLen--) {
            HashTable *p = HashSearch(w1, w2);
            if (!p) break;
    
            int n = (int)(rrand() * p->n);
            char *w3 = p->suffix[n];
    
            if (w3 == end) break;
            fprintf(fw, "%s ", w3);
            w1 = w2;
            w2 = w3;
        }
    
        fclose(fw);
    }
    
    // 分词函数
    void tokenize(char *book, char **list, int *count) {
        char buf[105];
        int i = 0, j = 0;
        while (i < strlen(book)) {
            if (book[i] > 32 && book[i] < 127) {
                j = 0;
                while (i < strlen(book) && book[i] > 32 && book[i] < 127)
                    buf[j++] = book[i++];
                buf[j] = '\0';
                list[*count] = strdup(buf);
                (*count)++;
            } else {
                i++;
            }
        }
    }
    
    // 释放内存
    void freeHashTable() {
        for (int i = 0; i < NHASH; i++) {
            HashTable *p = Hash[i];
            while (p) {
                HashTable *next = p->next;
                free(p->w1);
                free(p->w2);
                for (int k = 0; k < p->n; k++)
                    free(p->suffix[k]);
                free(p->suffix);
                free(p);
                p = next;
            }
        }
    }
    
    // 释放 list 数组
    void freeList(char **list, int count) {
        for (int i = 0; i < count; i++)
            free(list[i]);
    }
    
    int main() {
        FILE *fr = fopen("article.txt", "r");
        if (!fr) {
            fprintf(stderr, "Failed to open input file.\n");
            return 1;
        }
    
        fseek(fr, 0, SEEK_END);
        long len = ftell(fr);
        fseek(fr, 0, SEEK_SET);
        char *book = (char *)malloc(len + 1);
        fread(book, 1, len, fr);
        book[len] = '\0';
        fclose(fr);
    
        char *end = "(end)";
        char *list[1200000];
        int count = 0;
    
        tokenize(book, list, &count);
        free(book);
    
        for (int i = 0; i < count - 2; i++) {
            InsertHash(list[i], list[i + 1], list[i + 2]);
        }
    
        Write(1000); // 生成最多 1000 个词
    
        freeHashTable();
        freeList(list, count);
    
        return 0;
    }
    

    四、优化重点总结

    1. 修复内存泄漏:使用 strdup 替代直接赋值,避免野指针。
    2. 增强健壮性:增加对空指针和文件读取失败的判断。
    3. 改进哈希冲突处理:确保 HashConflict 仅基于 w2,提高准确性。
    4. 优化随机数生成:使用更稳定的方法生成随机数。
    5. 修复哈希表插入逻辑:确保 suffix 动态扩展时不会越界。
    6. 增加内存释放逻辑:防止内存泄漏,提升程序稳定性。

    如果你还有其他疑问或需要进一步优化,请随时告诉我!

    评论

报告相同问题?

问题事件

  • 创建了问题 今天