Ant Colony Optimization蚁群优化算法(ACO算法)概念及实战

Ant Colony Optimization

蚁群优化算法(ACO算法)

定义

蚁群算法(ant colony optimization, ACO),又称蚂蚁算法,是一种用来寻找优化路径的机率型算法。它由Marco Dorigo于1992年在他的博士论文中提出,其灵感来源于蚂蚁在寻找食物过程中发现路径的行为。

算法思想

  • 相互协作的一群蚂蚁可以战胜比自己强壮的昆虫,并把它搬回巢;而单个蚂蚁则不能。
  • 此外,蚂蚁还能够适应环境的变化,例如在蚁群的运动路线上突然出现障碍物时,它们能够很快地重新找到最优路径。

昆虫学家通过大量研究发现:蚂蚁个体之间是通过信息交流来找到从蚁巢到食物源的最短路径的

  1. 蚂蚁个体通过在其所经过的路上留下一种称之为“信息素”(pheromone)或“迹”的物质来实现与同伴之间的信息传递。
  2. 随后的蚂蚁遇到信息素时,不仅能检测出该物质的存在以及量的多少,而且可根据信息素的浓度来指导自己对前进方向的选择。

基本原理

蚁群算法的基本原理如下

  1. 蚂蚁在路径上释放信息素
  2. 碰到还没走过的路口,就随机挑选一条路走,同时,释放与路径长度有关的信息素
  3. 信息素浓度与路径长度成反比。后来的蚂蚁再次碰到该路口时,就有大概率选择信息素浓度较高的路径
  4. 最优路径上的信息素浓度越来越大
  5. 最终蚁群找到最优寻食路径

算法流程

在这里插入图片描述

我们将在例题中仔细介绍这一步步的流程,比如参数初始化时需要初始化什么参数,上图中的allowed是什么内容等等等等

例题

我们以旅行商问题(Traveling Salesman Problem TSP)为例,这里给不太了解什么是旅行商问题的同学解释一下什么是TSP问题

TSP问题:

给定n个城市,m条路径(无向边)连接这n个城市,每条路径(无向边)有一个权值代表两个城市之间的距离,现在要求旅行商把每一个城市都走一遍并回到原点,求路径的最短值。

在我们具体问题中 ,我们给定4个城市,每个城市之间都有一条路径(无向边),路径长度矩阵如下:
[ 0 2 6 3 2 0 4 1 6 4 0 8 3 1 8 0 ] \left[ \begin{array}{ll} 0 & 2 & 6 & 3\\ 2 & 0 & 4 & 1\\ 6 & 4 & 0 & 8\\ 3 & 1 & 8 & 0 \end{array} \right] 0263204164083180

参数初始化

我们假设旅行商(蚂蚁)数量m为2,城市数量n为4,城市i和城市j之间的距离为d[i][j] (i,j=1,2,…,n)

# 蚂蚁数
m = 2
# 城市数
n = 4
# 生成一张图
d = np.array([[0,2,6,3],
             [2,0,4,1],
             [6,4,0,8],
             [3,1,8,0]])

我们更新信息素浓度时需要一个常数Q(这个后面会提到);并且我们知道信息素在现实中是会随着时间挥发的,所以我们需要设定挥发率ρ;我们还要给每条路径上都设置一个基础的信息素浓度τ_0;以及初始化一个存储每条路径的信息素的一个二维数组τ

Q = 10
# 挥发率
rho = 0.5
#初始信息素浓度
tau_0 = 0.4
#初始化信息素浓度
tau = np.ones((n,n))*tau_0
# 自己到自己的信息素为0
for index in range(n):
    tau[index][index]=0

这里我们需要注意,不管是d还是τ,这两个二维数组都应当是对称矩阵,因为同一条路径不管是从i到j还是从j到i,一般情况下都是距离一样的,而且信息素浓度也应当一样。(除非有特殊情况,比如由于地形原因导致的路程不同等等,但是在这里我们不做考虑)

迭代

迭代之前我们需要确定终止的条件,在这里我们选用了迭代次数上限作为条件

我们为了记录迭代的变化,我们记录了每一轮迭代的得到的旅行商最短长度以及对应的路径

# 迭代次数
n_epoch = 2
# 记录每一轮迭代的最短长度
shortest_path_L_record = np.zeros(n_epoch)
# 记录每一轮迭代的最短长度的路径
shortest_path_record = np.zeros(shape=(n_epoch,n+1))
for epoch in range(n_epoch):
    # 迭代内容

在迭代前其实还需要一次参数的初始化,不过初始化的将是迭代时才需要的参数

迭代参数初始化

我们知道旅行商(蚂蚁)们是通过信息素得到概率加上轮盘赌来决定往哪里走的,这里我们可以记录下他们的概率p,(这个也可以不记录的,直接用临时变量做计算即可)

allowed是用来记录旅行商(蚂蚁)们当前可以往哪个城市(点)移动

current_cities 是用来记录旅行商(蚂蚁)们当前的位置,在一开始我们需要让他们随机分布在各个城市(点)

由于我们在旅行的最后需要返回起点,所以我们需要记录下起点的位置,于是便有了start_cities

在后面我们更新迹浓度时我们需要用到旅行商(蚂蚁)们每一次的移动的路线,于是我们有了move,其中move[i][j][k]的值代表第k个旅行商(蚂蚁)是否从城市(点)i移动到城市(点)j过

由于在这里我们示范的算法是ant-cycle蚁群算法,所以在后面更新迹浓度时还需要用到旅行商(蚂蚁)走过的总路程L

# 记录第k只蚂蚁从i往j走的概率
p = np.zeros(shape=(m,n,n))
# 记录第k只蚂蚁可以往哪个城市移动
allowed = np.ones(shape=(m,n))
# 记录第k只蚂蚁当前所在的城市的array 初始化时让他们处于随机城市
current_cities = np.array(np.floor(np.random.random(m)*n),dtype=np.int)
# 记录第k只蚂蚁出发的城市
start_cities = current_cities.copy()
# 我们需要记录每一次的移动的路径 (后面迹更新机制要用到)
move = np.zeros(shape=(n,n,m),dtype=np.int)
# 在allowed中把当前所在的城市设置为不可去 即值为0
for index in range(m):
    first_city = int(current_cities[index])
    allowed[index][first_city]=0
# L代表的是这个蚂蚁走过的总长度 如果对于下面这个L的用处有疑惑 我建议可以继续往下看
L  = np.zeros(shape=(m))
# 用于记录当前时刻
t=0
判断allowed是否为空集

如前面所说,allowed是用来记录旅行商(蚂蚁)们当前可以往哪个城市(点)移动的集合,但是我们的实现采用的是数组

allowed = np.ones(shape=(m,n))

当allowed[k][j]=1则说明,第k个旅行商(蚂蚁)还被允许往城市(点)j移动,

当allowed[k][j]=1则说明,第k个旅行商(蚂蚁)不被允许往城市(点)j移动。

按照这个思路,也就是allowed[k]的值全部为0,就说明第k个旅行商(蚂蚁)已经没地方可以去了

所以我们使用以下代码来实现对这个问题的判断

while allowed.max()!=0:
计算概率

当前旅行商(蚂蚁)在城市(点)i,假设只考虑信息素浓度对蚂蚁选择路径的影响:
P i j k ( t ) = { τ i j ( t ) ∑ s ∈ a l l o w e d k τ i s ( t ) , j ∈ a l l o w e d k 0 , j ∉ a l l o w e d k P_{ij}^k(t)= \left \{ \begin{array}{ll} \frac{\tau_{ij}(t)}{\sum_{s\in allowed_k}\tau_{is}(t)}, & j\in allowed_k\\ 0, & j\notin allowed_k \end{array} \right. Pijk(t)={sallowedkτis(t)τij(t),0,jallowedkj/allowedk

  • p[k][i][j] : t时刻第k只蚂蚁从点i转移到点ij的概率
  • s : 暂未访问的点i集合(allowed)中的某一个点i

由于
∑ s ∈ a l l o w e d k τ i s ( t ) \sum_{s\in allowed_k}\tau_{is}(t) sallowedkτis(t)
在一轮迭代中对于在同一个城市(点)出发的旅行商(蚂蚁)来说是固定的值,所以可以提前计算好

t+=1
for k in range(m):
    pk = p[k]
    i = int(current_cities[k])
    # 分母对于在一轮迭代中对于在同一个点出发的蚂蚁来说是固定的值
    summation_s_in_allowed_tau =  np.sum(tau[i][allowed[k]==1])
    for j in range(n):
        if allowed[k][j] == 0:
            pk[i][j]=0
        else:
            pk[i][j]= tau[i][j] /summation_s_in_allowed_tau

分析上述公式, 如果分叉选项太多的话容易导致收敛慢,我们考虑加入先验知识:比如主要走看起来近一些的路
P i j k ( t ) = { [ τ i j ( t ) ] α × [ 1 d i j ( t ) ] β ∑ s ∈ a l l o w e d k [ τ i s ( t ) ] α × [ 1 d i s ( t ) ] β , j ∈ a l l o w e d k 0 , j ∉ a l l o w e d k P_{ij}^k(t)= \left \{ \begin{array}{ll} \frac{[\tau_{ij}(t)]^\alpha \times [\frac{1}{d_{ij}(t)}]^\beta} {\sum_{s\in allowed_k}[\tau_{is}(t)]^\alpha \times [\frac{1}{d_{is}(t)}]^\beta}, & j\in allowed_k\\ 0, & j\notin allowed_k \end{array} \right. Pijk(t)= sallowedk[τis(t)]α×[dis(t)1]β[τij(t)]α×[dij(t)1]β,0,jallowedkj/allowedk
信息素浓度一样时,城市(点)i到城市(点)j的路径距离越小、1/d_{ij}(t)越大、概率值也就越大
其中α和 β 是为了调整信息素浓度先验知识对概率的影响,当α越大,信息素浓度影响越大,当β越大,先验知识影响越大

  • 当β为0 选择路径的概率完全由信息素浓度决定,为正反馈的启发式算法,缺点是收敛可能过慢
  • 当α为0 选择路径的概率完全由先验知识决定,为贪心算法,缺点是可能陷入局部最优解

这里我也把代码放在这里,大家可以参考着使用,但是在本篇文章中我们不使用这个概率计算方式。

#参数初始化
alpha = 2
beta  = 2
# 分母对于在一轮迭代中对于在同一个点出发的蚂蚁来说是固定的值
summation_s_in_allowed_tau_alpha_power_times_1_divided_by_dis_power_beta \
    =  np.sum(
        np.power(tau[i][allowed[k]==1],alpha)
        *np.power(1/d[i][allowed[k]==1],beta)
         )
#计算概率
if allowed[k][j] == 0:
    pk[i][j]=0
else:
	pk[i][j]= np.power(tau[i][j],alpha)*np.power(1/d[i][j],beta) /summation_s_in_allowed_tau_alpha_power_times_1_divided_by_dis_power_beta
轮盘赌决定下一个点 到达下一个点 更新信息

我们并非直接选择概率最大的路线作为我们旅行商(蚂蚁)的路线,而是选用轮盘赌的形式决定旅行商的去向,这样子使旅行商(蚂蚁)们的路线更多样化,不容易陷入局部最优。

效率起见,我们其实可以在计算p[k][i][j]时一边进行轮盘赌,如果赌到了后面的概率也不需要计算了,这样子的话可以节省很多时间,但是为了更为直观的体现我们的算法,这里我就不采取这种方法。

roulette =random.random()
current_probability = 0
for j in range(n):
    current_probability+=pk[i][j]
    # 如果赌到了
    if current_probability >roulette:
        # 记录一下路线
        move[i][j][k]=1
        # 更改一下当前点
        current_cities[k]=j
        # 记录一下已经来过j,allowed中去除j
        allowed[k][j]=0
        # 第k只蚂蚁总路线加长
        L[k]+=d[i][j]
        # 后面就不用赌了
        break
返回起点 更新信息

在整个allowed为空之后 ,注意题目的要求,题目要求是需要返回起点的,所以我们走完全程后还需要人为加上最后一步——返回起点

# 记得到达终点后还需要返回起点
for k in range(m):
    # 获取一开始我们保存的start_cities[k]
    start_city = start_cities[k]
    # 获取一开始蚂蚁们最后到达的城市
    final_city = current_cities[k]
    # 记录一下路线
    move[final_city][start_city][k]=1
    # 更改一下当前点
    current_cities[k]=start_city
    # 第k只蚂蚁总路线加长
    L[k]+=d[final_city][start_city]

更新迹浓度

迹浓度更新规则(ant-cycle算法)如下
τ i j ( t + 1 ) = ( 1 − ρ ) τ i j ( t ) + ∑ k = 1 m Δ τ i j k 其中 0 < ρ < 1 , Δ τ i j k = { Q L k , 第 k 只蚂蚁曾经过路径 i 到 j 0 , 其他 \tau_{ij}(t+1)=(1-\rho)\tau_{ij}(t)+\sum_{k=1}^m \Delta \tau_{ij}^k \\ 其中 0<\rho<1 ,\\ \Delta\tau_{ij}^k = \left \{ \begin{array}{ll} \frac{Q}{L_k}, &第k只蚂蚁曾经过路径i到j \\ 0, &其他 \end{array} \right. τij(t+1)=(1ρ)τij(t)+k=1mΔτijk其中0<ρ<1,Δτijk={LkQ,0,k只蚂蚁曾经过路径ij其他

  • ρ是挥发系数,ρ越大信息素挥发的越快
  • L_k 是第k只蚂蚁走过的总路径长度
  • Δτ_{ij}^k 是第k只蚂蚁路过i到j留下的信息素,这里我们选用的迹更新机制是ant-cycle,也就是以Q/L_k作为更新的值的机制,是蚁群算法提出者Marco Dorigo介绍的三种迹更新机制之一,还有两种分别是
    • ant-density算法 以Q作为更新的值的机制
    • ant-quantity算法 以Q/d_{ij}作为更新的值的机制

根据测试比较,其实是ant-cycle算法最好,因为它使用的是全局信息,而其它两个都是局部信息

for i in range(n):
    for j in range(n):
        # 注意 τ[i][j]的值是由move[i][j]和move[j][i]共同更新的,因为无论你从i到j还是从j到i 在i到j这段路程中迹浓度应当是一样的 前面有解释
        tau[i][j]=(1-rho)*tau[i][j]+np.sum(Q/L[move[i][j]==1])+np.sum(Q/L[move[j][i]==1])

我们一般会在更新迹浓度的时候顺带保存一下当前迭代轮次的最短路径以及对应的长度

# 从L中找到最短的那个的下标best_k
best_k = np.argmin(L)
# 声明一个用来装最短路径的1维数组,长度为n+1
shortest_path = np.zeros(shape=(n+1),dtype=np.int)
# 最后一位放上当前点,因为当前点必然是最后一站
shortest_path[n]=current_cities[best_k]
# 通过move数组依次逆推出完整路径
for index in range(n-1,-1,-1):
    j=shortest_path[index+1]
    shortest_path[index]=np.argmax(move.T[best_k][j])
# 记录下路径以及长度
shortest_path_L_record[epoch]=L[best_k]
shortest_path_record[epoch]=shortest_path
# 打印路径以及长度
print(f"epoch {epoch} shortest_path: {shortest_path} distance:{L[best_k]}")

找到最短路径

我们有了shortest_path_recordshortest_path_L_record两个数组后我们可以很轻易地得到最短的路径,我们只需要找shortest_path_L_record中的最小值的下标然后再shortest_path_record中查找路径即可

best_epoch = np.argmin(shortest_path_L_record)
shortest_path_definite = shortest_path_record[best_epoch]
print(f"best_epoch {best_epoch} shortest_path: {shortest_path_definite} distance:{shortest_path_L_record[best_epoch]}")

完整代码

import random
import numpy as np
# 蚂蚁数
m = 2
# 城市数
n = 4
# 生成一张图
d = np.array([[0,2,6,3],
             [2,0,4,1],
             [6,4,0,8],
             [3,1,8,0]])
Q = 10
# 挥发率
rho = 0.5
#初始信息素浓度
tau_0 = 0.4
#初始化信息素浓度
tau = np.ones((n,n))*tau_0
# 自己到自己的信息素为0
for index in range(n):
    tau[index][index]=0
# 迭代次数
n_epoch = 2
# 记录每一轮迭代的最短长度
shortest_path_L_record = np.zeros(n_epoch)
# 记录每一轮迭代的最短长度的路径
shortest_path_record = np.zeros(shape=(n_epoch,n+1))

for epoch in range(n_epoch):
    # 记录第k只蚂蚁从i往j走的概率
    p = np.zeros(shape=(m,n,n))
    # 记录第k只蚂蚁可以往哪个城市移动
    allowed = np.ones(shape=(m,n))
    # 记录第k只蚂蚁当前所在的城市的array 初始化时让他们处于随机城市
    current_cities = np.array(np.floor(np.random.random(m)*n),dtype=np.int)
    # 记录第k只蚂蚁出发的城市
    start_cities = current_cities.copy()
    # 我们需要记录每一次的移动的路径 (后面迹更新机制要用到)
    move = np.zeros(shape=(n,n,m),dtype=np.int)
    # 在allowed中把当前所在的城市设置为不可去 即值为0
    for index in range(m):
        first_city = int(current_cities[index])
        allowed[index][first_city]=0
    # L代表的是这个蚂蚁走过的总长度 如果对于下面这个L的用处有疑惑 我建议可以继续往下看
    L  = np.zeros(shape=(m))
    # 用于记录当前时刻
    t=0
    while allowed.max()!=0:
        t+=1
        for k in range(m):
            pk = p[k]
            i = int(current_cities[k])
            # 分母对于在一轮迭代中对于在同一个点出发的蚂蚁来说是固定的值
            summation_s_in_allowed_tau =  np.sum(tau[i][allowed[k]==1])
            for j in range(n):
                if allowed[k][j] == 0:
                    pk[i][j]=0
                else:
                    pk[i][j]= tau[i][j] /summation_s_in_allowed_tau
            roulette =random.random()
            current_probability = 0
            for j in range(n):
                current_probability+=pk[i][j]
                # 如果赌到了
                if current_probability >roulette:
                    # 记录一下路线
                    move[i][j][k]=1
                    # 更改一下当前点
                    current_cities[k]=j
                    # 记录一下已经来过j,allowed中去除j
                    allowed[k][j]=0
                    # 第k只蚂蚁总路线加长
                    L[k]+=d[i][j]
                    # 后面就不用赌了
                    break
    # 记得到达终点后还需要返回起点
    for k in range(m):
        # 获取一开始我们保存的start_cities[k]
        start_city = start_cities[k]
        # 获取一开始蚂蚁们最后到达的城市
        final_city = current_cities[k]
        # 记录一下路线
        move[final_city][start_city][k]=1
        # 更改一下当前点
        current_cities[k]=start_city
        # 第k只蚂蚁总路线加长
        L[k]+=d[final_city][start_city]
    for i in range(n):
        for j in range(n):
            # 注意 τ[i][j]的值是由move[i][j]和move[j][i]共同更新的,因为无论你从i到j还是从j到i 在i到j这段路程中迹浓度应当是一样的 前面有解释
            tau[i][j]=(1-rho)*tau[i][j]+np.sum(Q/L[move[i][j]==1])+np.sum(Q/L[move[j][i]==1])
    # 从L中找到最短的那个的下标best_k
    best_k = np.argmin(L)
    # 声明一个用来装最短路径的1维数组,长度为n+1
    shortest_path = np.zeros(shape=(n+1),dtype=np.int)
    # 最后一位放上当前点,因为当前点必然是最后一站
    shortest_path[n]=current_cities[best_k]
    # 通过move数组依次逆推出完整路径
    for index in range(n-1,-1,-1):
        j=shortest_path[index+1]
        shortest_path[index]=np.argmax(move.T[best_k][j])
    # 记录下路径以及长度
    shortest_path_L_record[epoch]=L[best_k]
    shortest_path_record[epoch]=shortest_path
    # 打印路径以及长度
    print(f"epoch {epoch} shortest_path: {shortest_path} distance:{L[best_k]}")
    
best_epoch = np.argmin(shortest_path_L_record)
shortest_path_definite = shortest_path_record[best_epoch]
print(f"best_epoch {best_epoch} shortest_path: {shortest_path_definite} distance:{shortest_path_L_record[best_epoch]}")
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 蚁群优化算法 (Ant Colony Optimization, ACO) 是一种模拟蚂蚁行为的优化算法。它可以应用于寻找最优解的问题。 对于 XGBoost,蚁群优化算法可以提升模型的性能。 XGBoost 通常通过交叉验证和网格搜索来确定最佳超参数。然而,这个过程可能是非常缓慢和复杂的,特别是当超参数空间很大时。蚁群优化算法可以加速这个过程,并且更快地找到更好的超参数组合。 蚁群优化算法是基于蚂蚁的行为来模拟搜索过程。蚁群中的每只蚂蚁都是一个独立的搜索代理,它们通过在超参数空间中选择最优的解来搜索最佳解。在蚁群中,蚂蚁之间的信息交流和协作有助于更快地找到全局最优解。 因此,在 XGBoost 中使用蚁群优化算法可以带来显著的提升,因为它可以更快地确定最佳超参数,从而提高模型的性能。 ### 回答2: 蚁群优化算法是受到蚂蚁觅食行为启发而提出的一种启发式优化算法。它通过模拟蚂蚁在觅食时释放信息素和在路径选择中的正反馈行为,以寻找最优解。蚁群优化算法能够较好地应用于组合优化问题,并已在多个领域取得了成功。 将蚁群优化算法应用于XGBoost模型中,可以带来以下几方面的提升: 首先,蚁群优化算法可以改善XGBoost模型的参数调优效果。XGBoost模型中的参数选择对模型的性能影响较大,而蚁群优化算法能够通过搜索参数空间来找到更优的参数组合,提高模型的预测准确性。 其次,蚁群优化算法可以加速XGBoost模型的训练过程。XGBoost模型的训练过程需要经过多轮迭代,而蚁群优化算法可以通过并行计算和信息素更新策略,快速搜索到较优的解,从而加速模型的训练过程,提高效率。 此外,蚁群优化算法还可以解决XGBoost模型中存在的过拟合问题。XGBoost模型在处理复杂问题时容易过拟合,而蚁群优化算法能够通过信息素的正反馈机制,在搜索过程中引入随机性,有效地减少模型的过拟合程度,使模型更具泛化能力。 综上所述,蚁群优化算法对XGBoost模型的提升主要体现在参数调优、训练速度和过拟合问题上。通过应用蚁群优化算法,可以提高XGBoost模型的性能和效率,提升其应用范围和实际效果。 ### 回答3: 蚁群优化算法是一种模拟蚂蚁寻找食物的行为模式而设计的优化算法。它与XGBoost这种梯度提升决策树算法相结合可以带来一些提升。 首先,蚁群优化算法与XGBoost都属于集成学习的范畴,它们通过结合多个弱分类器来提高整体预测性能。蚁群优化算法通过模拟蚂蚁在搜索过程中的信息交流和集体智慧,能够帮助XGBoost更有效地组合弱分类器并学习出更好的模型。 其次,蚁群优化算法的搜索方式与XGBoost的梯度提升决策树算法互补。XGBoost通过优化损失函数对模型进行训练,但可能会陷入局部最优解。而蚁群优化算法通过启发式搜索,在解空间中能够绕过局部最优解,以全局最优解为目标进行搜索,提升了整体搜索性能。 另外,蚁群优化算法引入了正反馈机制和信息素的概念蚁群中的蚂蚁会释放信息素来标记已经发现的较好路径,其他蚂蚁可以通过这些信息素来选择路径。这种正反馈机制能够帮助XGBoost避免陷入局部最优解,并引导模型朝着更优的方向进行学习。 最后,蚁群优化算法还具有并行处理能力,可以加速算法的执行速度。在XGBoost中,通过并行化处理可以提高模型训练的效率,同时蚁群优化算法的并行处理特性可以更好地发挥出来,缩短算法的运行时间。 综上所述,蚁群优化算法对XGBoost的提升主要体现在两个方面:一是通过全局搜索能够避免局部最优解,帮助XGBoost找到更好的模型;二是通过正反馈机制和并行处理提高了算法的效率。这些优势使得蚁群优化算法能够更好地与XGBoost相结合,提升整体性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值