贪心算法的改进

本文介绍了一种改进的贪心算法,通过分解极大强连通子图寻找影响力最大的节点,降低算法复杂度。同时,提出了改进的LT模型,通过计算边权重和节点AP值,提高模型可靠性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于贪心算法,请看我的上一篇博客。

      1. 解决贪心算法的复杂度

为解决贪心算法的复杂度。本文提出:通过分解极大联通子图去寻找影响力最大的节点的算法。

强连通:在有向图G中,如果任意两个不同的顶点相互可达,则称该有向图是强连通的。

极大强连通子图:G 是一个极大强连通子图当且仅当 G 是一个强连通子图且不存在另一个强连通子图 G’使得 G 是 G’的真子集。也就是说不可能找到一个包含它的强连通分量。若将有向图中的强连通分量都缩为一个点,则原图会形成一个 DAG(有向无环图)

该改进算法是基于对图深度优先搜索(DFS)的算法,每个强连通分量为搜索树中的一棵子树(的一部分)。搜索时,把当前搜索树中未处理的结点加入一个栈,对于每一个结点,当它自己搜索完毕时判断是否存在强连通分量。

算法:void dfs(int u)

{

    times++;//记录dfn顺序

    dfn[u]=times;//赋值

    low[u]=times;//先赋初值

    vis[u]=true;//vis[i]用来判断i是否搜索过;

    insta[u]=true;//表示是否在栈中,true为在栈中;

    stack[top]=u;//栈顶

    top++;

    for(int i=head[u];i!=-1;i=edge[i].next)// 以建图顺序枚举此点所连的边

    {

        int v=edge[i].to;//搜索到的点

        if(!vis[v])//如果未搜索过即未入栈

        {

            dfs(v);//继续以此点进行深搜

            low[u]=min(low[u],low[v]);//更新low值,此边为树枝边所以比较u此时的

        }                           // low值(未更新时就是其dfn值)和v的low值

        else

            if(insta[v]==true)//如果搜索过且在栈中,说明此边为后向边或栈中横叉边

            {

                low[u]=min(low[u],dfn[v]);//更新low值,比较u此时的low值和v的dfn值

            }

    }

    if(low[u]==dfn[u])//相等说明找到一个强连通分量

    {

        while(top>0&&stack[top]!=u)//开始退栈一直退到 u为止

        {

            top--;

            insta[stack[top]]=false;

        }

    }

}

分解完之后再用上述贪心算法。

      1. 改进LT模型,重复使用贪心算法

在LT模型的基础上:计算边的权重,计算每个节点AP的值,选择前k/2个节点,贪心算法再选择后k/2个节点。减小了计算的复杂度,增加权重和AP值使模型更可靠。

#计算图中边的权重

def Buv_calculate(G,u,v):

    out_deg_all = G.out_degree()  # 获取所有节点的出度

    in_edges_all = G.in_edges()  # 获取所有的入边

    out_deg = out_deg_all[u]  # 获取节点e[0]的出度

    in_edges = in_edges_all._adjdict[v]  # 获取节点e[1]的所有的入边

    edges_dict = dict(in_edges)

    in_all_edges = list(edges_dict.keys())  # 获取节点e[1]的所有入边节点并存入列表

    out_deg_sum = 0

    for i in in_all_edges:  # 求节点e[1]所有入边节点的出度和

        out_deg_sum = out_deg_sum + out_deg_all[i]

    return out_deg / out_deg_sum

 

#计算每个节点AP的值

def AP_calculate(node):

    data = []

    data.append(node)

    layer_two_nodes = linear_threshold(G, data, 2)  # 获取每个节点的两层出度节点数

    data.pop()

    del layer_two_nodes[-1]

    length = 0

    for i in range(len(layer_two_nodes)):

        length = length + len(layer_two_nodes[i])

    lengths = length - len(layer_two_nodes[0])

 

    out_edges = out_edges_all._adjdict[node]  # 获得节点的出边

    edges_dict = dict(out_edges)

    out_all_edges = list(edges_dict.keys())  # 将节点的所有出边存入列表

    Buv_sum = 0

    for out_edge in out_all_edges:   # 计算该节点所有出边的Buv的值

        Buv = Buv_calculate(G, node, out_edge)

        Buv_sum = Buv_sum + Buv

    cha_sum = 1 + math.e ** (-Buv_sum)

    AP = lengths + cha_sum

    return AP

 

def select_layers(G,node_list_sorted,k1):   #选择前k/2个节点的算法实现

    seed_nodes = []  # 存贮种子节点

    for i in range(k1):

        data = []

        data.append(node_list_sorted[0][0])

        seed_nodes.append(node_list_sorted[0][0])

        layers = linear_threshold(G, data)        # 使用LT算法

        data.pop()

        del layers[-1]

        layers_activate = []

        for i in layers:  # 将种子节点和激活的节点存入layers_activate列表

            for j in i:

                layers_activate.append(j)

 

        for m in node_list_sorted:  # 删除node_list_sorted中的layers_activate

            for n in layers_activate:

                if m[0] == n:

                    node_list_sorted.remove(m)

 

    return seed_nodes,node_list_sorted

 

def _select_others(seed_nodes, other_nodes,k2):     #贪心算法选择剩余k/2个节点

    for m in range(k2):

        all_nodes = list(other_nodes)   #将所有的节点存储在all_nodes列表里

        layers_activite = []    # 存储每个节点的激活节点列表

        lengths = []         # 存储每个节点的激活列表长度

        datas = []

        for i in all_nodes:   #遍历所有的节点,分别求出每个节点对应的激活节点集以及激活节点集的长度

            data = []

            data.append(i)

            datas.append(i)

            data_test=seed_nodes+data

            layers = linear_threshold(G,data_test)

            data.pop()

            del layers[-1]

            length = 0

            layer_data = []

            for j in range(len(layers)):

                length = length + len(layers[j])

                layer_data = layer_data + layers[j]

            length_s = length - len(layers[0])

            for s in range(len(layers[0])):

                del layer_data[0]

            layers_activite.append(layer_data)

            lengths.append(length_s)

        layers_max = layers_activite[lengths.index(max(lengths))]  # 获取被激活节点数最多的列表

        seed_nodes.append(datas[lengths.index(max(lengths))])      # 获取被激活节点最多的子节点

        for i in all_nodes:       #该循环是删除所有节点中seed_nodes节点集

            if i in seed_nodes:

                del all_nodes[all_nodes.index(i)]

        other_nodes=all_nodes

    return seed_nodes,layers_max     #返回值是贪心算法求得的子节点集和该子节点集激活的最大节点集

### 如何对贪心算法进行改进 #### 基本概念回顾 贪心算法是一种通过在每一步选择中都采取当前看起来最佳的选择来解决问题的方法[^2]。然而,这种方法容易陷入局部最优解而非全局最优解。因此,在实际应用中,通常需要引入额外的技术或策略对其进行改进。 --- #### 使用爬山法改进贪心算法的结果 一种常见的改进方式是利用爬山法(Hill Climbing Algorithm)。该方法可以在贪心算法得出初步解之后,尝试对其进一步优化。具体而言,爬山法会从初始解出发,不断探索邻近区域以寻找更优的解决方案。如果发现新的解优于现有解,则更新当前解并继续迭代直到无法再改善为止[^1]。 以下是实现此过程的一个伪代码示例: ```python def hill_climbing(initial_solution, evaluate_function, neighbors_function): current = initial_solution while True: best_neighbor = None best_value = evaluate_function(current) for neighbor in neighbors_function(current): # 遍历邻居节点 value = evaluate_function(neighbor) if value > best_value: # 找到更好的邻居 best_neighbor = neighbor best_value = value if best_neighbor is None: # 如果没有更好邻居则停止 break current = best_neighbor return current ``` 上述代码展示了如何基于初始解 `initial_solution` 和评估函数 `evaluate_function` 来逐步提升解的质量。注意这里的 `neighbors_function` 定义了一个用于生成候选解集合的操作集。 --- #### 构造高效的贪心策略 除了后续优化外,还可以专注于设计更加合理的贪心策略本身。这涉及两方面的工作: 1. **贪婪选择属性**:确保每次做出的选择都是当下最有利的那个选项; 2. **最优子结构**:验证问题是否能够分解成若干相互独立的小规模子问题,并且这些子问题的最优解组合起来能构成原问题的整体最优解。 例如,在分配资源类问题中,可以通过预处理数据或者调整匹配逻辑降低冗余操作带来的性能损耗。下面是一个关于儿童分发饼干的例子说明这一点[^3]: 原始版本可能存在重复检测已标记项的情况,从而影响效率。经过修改后的程序如下所示: ```java public int findContentChildren(int[] g, int[] s) { if (s.length == 0) { return 0; } Arrays.sort(g); Arrays.sort(s); int total = 0; int startPos = 0; for (int i = 0; i < g.length; i++) { boolean found = false; for (int j = startPos; j < s.length; j++) { if (s[j] >= g[i]) { total++; startPos = j + 1; // 更新起始位置 found = true; break; } } if (!found && startPos >= s.length){ break; } } return total; } ``` 这里通过记录上次成功配对的位置 (`startPos`) 减少了不必要的遍历次数,显著提高了运行时间表现。 --- #### 结合其他启发式技术 对于某些复杂场景来说,单纯依赖单一技巧可能不足以获得满意的效果。此时可考虑融合多种元启发式算法如模拟退火(SA),遗传算法(GA) 或禁忌搜索(TS) 等共同作用于同一目标之上形成混合型求解框架。这类方案往往具备更强健性和适应能力适用于解决更大范围内的难题实例。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值