【问题标题】:How to store a decision tree while I'm building it in C?我在 C 中构建决策树时如何存储它?
【发布时间】:2013-04-13 15:30:42
【问题描述】:

这里是概念问题。

我正在递归地构建决策树。函数的每次迭代都采用训练示例的子集,遍历所有特征和每个特征中所有可能的拆分,找到可能的最佳拆分,将子集拆分为两个较小的子集,并调用自身(函数)两次,每次一个拆分子集。

我之前在 MatLab 中对此进行了编码,但它运行得太慢了,所以现在我在 C 中尝试它(我不太熟悉)。在 MatLab 中,我使用了一个全局“拆分”矩阵来保存每个拆分的信息(哪个特征,该特征中的什么值,如果这是一个叶节点,分类是什么,每个子节点的行号),并且这样我就可以使用新的测试数据点遍历该矩阵以找到它的分类。

C 中的全局二维数组看起来可以使用头文件,但如果有另一种方法,我宁愿不进入头文件。问题是,因为函数是递归调用的,所以我很难知道“拆分”中的下一个可用行是什么。我可以做一些事情,比如孩子的行是父行的 2*i 和 2*i+1,但是对于有很多拆分的大型数组,这将需要大量的初始存储。

有什么想法吗?

【问题讨论】:

    标签: c machine-learning


    【解决方案1】:

    听起来你必须放弃二维数组来表示你的树。 C 中任意度数的树通常如下所示:

    struct node
    {       struct node ** children;
            int num_children;
            /* Values in the node/leafs */
    };
    

    如果树的度数是固定的,或者每个节点都有一个最大值,那么下面会做

    struct node
    {       struct node * children;
            int num_children; /* If degree has only a maximum */
            /* Values in the node/leafs */
    };
    

    您将不得不使用malloc 和朋友为节点及其子节点分配内存。

    关于头文件:头文件是福(在C语言中),不是祸,但如果你坚持不做,那么他们总是可以替换他们的#include实例。

    如果您要从 MatLab 转向另一种语言以加快实现速度,那么您可能需要首先考虑 C 以外的其他语言。 Java、Python 甚至 Haskell 等语言可能会为您提供类似的加速,但在所有指针方面都不会那么麻烦。

    【讨论】:

    • 啊,好吧是有道理的。我对结构有一些经验,但出于某种原因从未考虑过一棵树。非常感谢。
    【解决方案2】:

    在 C 中使用这种函数式设计并不漂亮,因为不能保证递归调用会被优化为循环,并且没有匿名函数。至少 C++ 中有 lambda;我建议 C++ 更适合于此,尽管 AFAIK 仍然不能保证 C++ 中的优化。

    为了避免可能导致堆栈增长的递归,每个分支都需要返回它选择的下一个分支。调用者(main)然后循环返回值,并在返回值是我们的终端值时终止循环。我们将分支类型定义为函数指针:

    typedef void branch();
    

    然后我们声明每个实际分支返回一个分支类型:

    branch initial(void) {
        /* do initial processing */
        srand(time(NULL));
        int x = rand() % 2;
        return x == 0 ? left : right;
    }
    
    branch terminal(void) {
        /* This should never be called */
        assert(0);
        return NULL;
    }
    
    branch left(void) {
        /* do left processing */
        return terminal; /* return a terminal branch to indicate no further
                          * processing */
    }
    
    branch right(void) {
        int x;
        /* do right processing, storing either a 0 in x to indicate right_left
         * as the selected branch, or 1 in x to indicate right_right...
         */
        return x == 0 ? right_left : right_right;
    }
    
    branch right_left(void) {
        /* do right_left processing */
        return initial; /* return initial to repeat that branch */
    }
    
    branch right_right(void) {
        /* do right_right processing; */
        return right; /* return right to repeat that branch */
    }
    

    ... 并在返回值上循环看起来像这样:

    int main(void) {
        branch *(b)(void) = initial;
        while (b != terminal) {
            b = b();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-05-19
      • 2014-03-14
      • 1970-01-01
      • 2016-07-27
      • 2021-03-02
      • 2021-04-22
      • 2021-12-22
      • 2019-10-02
      • 1970-01-01
      相关资源
      最近更新 更多