MARKOV算法

【转自】http://www.yuanma.org/data/2007/1220/article_2928.htm

 

在<<the practice of programming>>第三章中,两位作者举了一个markov算法的例子,文章一开头,作者以Frederick P. Brook的名言来说明了数据结构的重要性-"如果你只给我看流程图,不给我看你的数据结构表,我可能还是很困惑;如果你给我看了你的数据结构表,流程图有些时候并不重要,流程太明显了"。

两位作者举了一个非常有趣的例子,利用文本中已经有的文本组合成新的文本。

需要注意的是,作者没有初始化hash数组。以下是一些亮点:
1. 判断两个数组相等的技法
2. 正确在链表中随机选择一个节点的方法
3. hash技法
4. 如何开始,如何结束
5. 开始时,并没有两个previous,巧妙地使用了文本中不存在的字符当作previous,任何输入的字符都是suffix.
6. 最后一个节点没有suffix,使用文本中不存在的字符当做suffix.

平时自己写了太多的C++程序,经过这个程序,C语言功力有点提升。

// markov.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <assert.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>

enum 
{
    NHASH = 5000,
    MULTIPLIER = 37,
    NPREV = 2
};

typedef struct Suffix Suffix;
typedef struct State State;
struct State
{
    char *prev[NPREV];
    Suffix *suf;
    State *next;
};

char NOWORD[] = "\n";

struct Suffix
{
    char *word;
    Suffix *next;
};

State *sHash[NHASH];


int hash(char *prev[NPREV])
{
    assert(prev);
    
    int h = 0;
    for (int i = 0; i < NPREV; ++i)
        for (char *p = prev[i]; *p != '\0'; ++p)
            h = MULTIPLIER * h + *p;

    h = h % NHASH;
    if (h < 0) //防止负数溢出
        h = h + NHASH;

    return h;
}

State *LookUp(char *prev[NPREV], int create)
{
    int h = hash(prev);
    State *sp = NULL;
    for (sp = sHash[h]; sp != NULL; sp = sp->next)
    {
        int i = 0;
        for (; i < NPREV; ++i)
        {
            if (strcmp(sp->prev[i], prev[i]) != 0)
                break;
        }

        if (i == NPREV)
            return sp;
    }

    if (create == 1)
    {
        sp = (State*)malloc(sizeof(State));
        sp->suf = NULL;
        for (int i = 0; i < NPREV; ++i)
            sp->prev[i] = prev[i];
        sp->next = sHash[h];
        sHash[h] = sp;

        return sp;
    }

    return NULL;
}

void AddSuffix(State *pState, char *suffix)
{
    assert(pState && suffix);
    Suffix *pSuffix = (Suffix*)malloc(sizeof(Suffix));
    pSuffix->word = suffix;
    pSuffix->next = pState->suf;
    pState->suf = pSuffix;
}

void Add(char *prev[NPREV], char *suffix)
{
    State *s = LookUp(prev, 1);
    if (!s)
        return;

    AddSuffix(s, suffix);
    memmove(prev, prev + 1, (NPREV - 1) * sizeof(prev[0]));
    prev[NPREV - 1] = suffix;
}

void Build(char *prev[NPREV], FILE *f)
{
    char buf[100], fmt[10];
    sprintf(fmt, "%%%ds", sizeof(buf) - 1);
    while (fscanf(f, fmt, buf) != EOF)
    {
        Add(prev, strdup(buf));
    }
}

void Clear()
{
}

void Generate(int nwords)
{
    char *prev[NPREV];
    char *w;
    for (int i = 0; i < NPREV; ++i)
        prev[i] = NOWORD;

    State *sp = NULL;

    for (int i = 0; i < nwords; ++i)
    {
        sp = LookUp(prev, 0);

        int nmatch = 0;
        for (Suffix *pSuf = sp->suf; pSuf != NULL; pSuf = pSuf->next)
        {
            if (rand() % ++nmatch == 0)
                w = pSuf->word;
        }

        if (strcmp(w, NOWORD) == 0)
                break;

        printf("%s\n", w);
        memmove(prev, prev + 1, (NPREV - 1) * sizeof(prev[0]));
        prev[NPREV - 1] = w;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    srand(time(NULL));
    FILE *f = fopen("c:\\a.txt", "r");
    if (f == NULL)
        goto END;

    char *prev[NPREV] = {
        NOWORD,
        NOWORD
    };

    for (int i = 0; i < NHASH; ++i)
        sHash[i] = NULL;

    Build(prev, f);
    Add(prev, NOWORD);
    
    Generate(1000);

    Clear();
    fclose(f);

END:
    system("pause");
    return 0;
}

posted on 2012-02-20 22:10  ykf23  阅读(460)  评论(0编辑  收藏  举报