使用梯形法计算随机函数的定积分

支持组合函数,可由基本运算符、三角函数、对数函数、指数函数、括号运算符组成。

例:

// String function = "f(x)=12*(x^3*secx)+3";
// String function = "f(x)=1/x";
// String function = "f(x)=log2(x^3)";
// String function = "f(x)=tanx/2*(x^3)";
// String function = "f(x)=5*(x^3)*sinx+(5*cosx*(x^2))";
// String function = "f(x)=lg(4*x)+((x^3)/5))";
// String function = "f(x)=lnx*4/x+200-x";

// String function = "g(x)=-(x^2)+(x^3)";


核心代码:

/**
 * 常用数学计算,主要用于支持定积分。
 * 使用说明:
 * 1.函数中避免使用e、pi这类参数。
 * 2.针对有计算优先级的请加括号。
 * 3.使用'x'作为指定变量,请在函数表达式中不要在其他位置使用'x'。
 * @author: ulrika
 * @since: 2018/5/18
 */
@Component
public class MathUtil {

   private static final double pi = Math.PI;

   private static final double e = Math.E;

   /**
    * 采用梯形法计算定积分,支持函数中有加、减、乘法(*)、除法(-)、简单的三角函数、对数、指数、绝对值、括号的运算。
    * @param function 函数,支持以"f(x)="或"g(x)="或直接开头的函数
    * @param down 积分下限
    * @param up 积分上限
    * @param precision 分割精度(分割次数)
    * @param isRadians 是否是弧度
    * @return
    */
   public static double computeDefiniteIntegration(String function,double down,double up,int precision,boolean isRadians) throws Exception{
      DecimalFormat decimalFormat = new DecimalFormat("##0.00000000");
      if(function.contains("=")){
         function = function.substring(function.indexOf('=')+1,function.length());
      }
      double result = 0;Double piece =(up-down)/precision;
      for(Double downCopy = down;downCopy<up-piece;downCopy+=piece){
         String function1 = function.replace("x",decimalFormat.format(downCopy));
         String function2 = function.replace("x",decimalFormat.format(downCopy+piece));
         result+=(executeExpression(function1,isRadians)+executeExpression(function2,isRadians))*piece/2;
      }
      return result;
   }

   /**
    * 执行一个表达式
    * @param expression
    * @param isRadians
    * @return
    */
   public static double executeExpression(String expression,boolean isRadians) throws Exception{
      if(StringUtils.isEmpty(expression)){
         return 0;
      }
      double result = 0;
      char[] expressionChars = expression.toCharArray();
      boolean isFirstCharacter = true;
      int i = 0;
      //如果以数字开头,且执行加减法,则对首个数字进行加法
      if(stringIsNumeric(expression.substring(0,1))&&isFirstCharacter==true){
         int index = nextOperatorIndex(expressionChars,0);
         if(expression.length()==index){
            result = plus(result,Double.parseDouble(expression.substring(0,index)));
            i += index;
         }else{
            result = plus(result,Double.parseDouble(expression.substring(0,index)));
            i += index;
         }
         isFirstCharacter = false;

      }
      for(;i<expressionChars.length;i++){
         if(expressionChars[i]=='s'||expressionChars[i]=='c'||expressionChars[i]=='t'||expressionChars[i]=='l'){
            Map<String,Double> functionResult;
            if(expressionChars[i]=='l'&&(expressionChars[i+1]=='n'||expressionChars[i+1]=='g')){
               functionResult = executeFunction(expression.substring(i,i+2+handleLength(expression.substring(i+2))),isRadians);
            }else if(expressionChars[i]=='l'&&expressionChars[i+1]=='o'&&expressionChars[i+2]=='g'){
               int firstOffset = handleLength(expression.substring(i+3));
               functionResult = executeFunction(expression.substring(i,i+3+firstOffset+handleLength(expression.substring(firstOffset+i+3))),isRadians);
            }else{
               functionResult = executeFunction(expression.substring(i,i+3+handleLength(expression.substring(i+3))),isRadians);
            }
            result += functionResult.get("result");
            i += functionResult.get("index");
            if(isFirstCharacter){
               isFirstCharacter = false;
            }
         }
         switch(expressionChars[i]){
            case '+':
               if(isFirstCharacter){
                  isFirstCharacter = false;
               }
               if(expressionChars[i+1]=='s'||expressionChars[i+1]=='c'||expressionChars[i+1]=='t'||expressionChars[i+1]=='l'){
                  Map<String,Double> functionResult;
                  if(expressionChars[i+1]=='l'&&(expressionChars[i+2]=='n'||expressionChars[i+2]=='g')){
                     functionResult = executeFunction(expression.substring(i+1,i+3+handleLength(expression.substring(i+3))),isRadians);
                  }else{
                     int firstOffset = handleLength(expression.substring(i+4));
                     functionResult = executeFunction(expression.substring(i+1,i+4+firstOffset+handleLength(expression.substring(i+4+firstOffset))),isRadians);
                  }
                  result += functionResult.get("result");
                  i += functionResult.get("index");
                  break;
               }
               if(expressionChars[i+1]=='('){
                  Map<String,Double> bracketResult = computeBracket(expression.substring(i+1,i+1+handleLength(expression.substring(i+1))),isRadians);
                  result += bracketResult.get("result");
                  i += bracketResult.get("index");
               }else{
                  int nextOperatorIndex1 = 0;
                  Double tar1 = 0D;
                  if(isFirstCharacter){
                     nextOperatorIndex1 = nextOperatorIndex(expressionChars,i);
                     int nextOperator1 = nextOperatorIndex(expressionChars,nextOperatorIndex1+1);
                     tar1 = Double.parseDouble(expression.substring(nextOperatorIndex1+1,nextOperator1));
                     i += expression.substring(nextOperatorIndex1+1,nextOperator1).length();
                  }else{
                     nextOperatorIndex1 = nextOperatorIndex(expressionChars,i+1);
                     tar1 = Double.parseDouble(expression.substring(i+1,nextOperatorIndex1));
                     i += expression.substring(i+1,nextOperatorIndex1).length();
                  }
                  result = plus(result,tar1);
               }
               break;
            case '-':
               if(expressionChars[i+1]=='s'||expressionChars[i+1]=='c'||expressionChars[i+1]=='t'||expressionChars[i+1]=='l'){
                  Map<String,Double> functionResult;
                  if(expressionChars[i+1]=='l'&&(expressionChars[i+2]=='n'||expressionChars[i+2]=='g')){
                     functionResult = executeFunction(expression.substring(i+1,i+3+handleLength(expression.substring(i+3))),isRadians);
                  }else{
                     int firstOffset = handleLength(expression.substring(i+4));
                     functionResult = executeFunction(expression.substring(i+1,i+4+firstOffset+handleLength(expression.substring(i+4+firstOffset))),isRadians);
                  }
                  result -= functionResult.get("result");
                  i += functionResult.get("index");
                  break;
               }
               if(expressionChars[i+1]=='('){
                  Map<String,Double> bracketResult = computeBracket(expression.substring(i+1,expression.length()),isRadians);
                  result -= bracketResult.get("result");
                  i += bracketResult.get("index");
               }else{
                  int nextOperatorIndex2 = 0;
                  Double tar2 = 0D;
                  if(isFirstCharacter){
                     nextOperatorIndex2 = nextOperatorIndex(expressionChars,i);
                     int nextOperator2 = nextOperatorIndex(expressionChars,nextOperatorIndex2+1);
                     tar2 = Double.parseDouble(expression.substring(nextOperatorIndex2+1,nextOperator2));
                     i += expression.substring(nextOperatorIndex2+1,nextOperator2).length();
                  }else{
                     nextOperatorIndex2 = nextOperatorIndex(expressionChars,i+1);
                     tar2 = Double.parseDouble(expression.substring(i+1,nextOperatorIndex2));
                     i += expression.substring(i+1,nextOperatorIndex2).length();
                  }
                  result = minus(result,tar2);
               }
               if(isFirstCharacter){
                  isFirstCharacter = false;
               }
               break;
            case '*':
               double src1 = executeExpression(expression.substring(previousOperatorIndex(expressionChars,i),i),isRadians);
               if(expressionChars[i+1]=='s'||expressionChars[i+1]=='c'||expressionChars[i+1]=='t'||expressionChars[i+1]=='l'){
                  Map<String,Double> functionResult;
                  if(expressionChars[i+1]=='l'&&(expressionChars[i+2]=='n'||expressionChars[i+2]=='g')){
                     functionResult = executeFunction(expression.substring(i+1,i+3+handleLength(expression.substring(i+3))),isRadians);
                  }else{
                     int firstOffset = handleLength(expression.substring(i+4));
                     functionResult = executeFunction(expression.substring(i+1,i+4+firstOffset+handleLength(expression.substring(i+4+firstOffset))),isRadians);
                  }
                  result = computeValue('*',result,functionResult.get("result"));
                  i += functionResult.get("index");
                  break;
               }
               int nextOperatorIndex3 = i+1+handleLength(expression.substring(i+1));
               if(expressionChars[i+1]=='('){
                  Map<String,Double> bracketResult = computeBracket(expression.substring(i+1,nextOperatorIndex3),isRadians);
                  result = computeValue('*',result,bracketResult.get("result"));
                  i += bracketResult.get("index");
               }else{
                  Double tar3 = executeExpression(expression.substring(i+1,nextOperatorIndex3),isRadians);
                  result = computeValue('*',result,tar3);
                  i += expression.substring(i+1,nextOperatorIndex3).length();
               }
               if(isFirstCharacter){
                  isFirstCharacter = false;
               }
               break;
            case '/':
               double src2 = executeExpression(expression.substring(previousOperatorIndex(expressionChars,i),i),isRadians);
               if(expressionChars[i+1]=='s'||expressionChars[i+1]=='c'||expressionChars[i+1]=='t'||expressionChars[i+1]=='l'){
                  Map<String,Double> functionResult;
                  if(expressionChars[i+1]=='l'&&(expressionChars[i+2]=='n'||expressionChars[i+2]=='g')){
                     functionResult = executeFunction(expression.substring(i+1,i+3+handleLength(expression.substring(i+3))),isRadians);
                  }else{
                     int firstOffset = handleLength(expression.substring(i+4));
                     functionResult = executeFunction(expression.substring(i+1,i+4+firstOffset+handleLength(expression.substring(i+firstOffset+4))),isRadians);
                  }
                  result = computeValue('/',result,functionResult.get("result"));
                  i += functionResult.get("index");
                  break;
               }
               int nextOperatorIndex4 = i+1+handleLength(expression.substring(i+1));
               if(expressionChars[i+1]=='('){
                  Map<String,Double> bracketResult = computeBracket(expression.substring(i+1,nextOperatorIndex4),isRadians);
                  result = computeValue('/',result,bracketResult.get("result"));
                  i += bracketResult.get("index");
               }else{
                  Double tar4 = executeExpression(expression.substring(i+1,nextOperatorIndex4),isRadians);
                  result = computeValue('/',result,tar4);
                  i += expression.substring(i+1,nextOperatorIndex4).length();
               }
               if(isFirstCharacter){
                  i--;
                  isFirstCharacter = false;
               }
               break;
            case '^':
//             int previousOperatorIndex = previousOperatorIndex(expressionChars,i);
//             double value = executeExpression(expression.substring(previousOperatorIndex,i),isRadians);
               if(expressionChars[i+1]=='s'||expressionChars[i+1]=='c'||expressionChars[i+1]=='t'||expressionChars[i+1]=='l'){
                  Map<String,Double> functionResult;
                  if(expressionChars[i+1]=='l'&&(expressionChars[i+2]=='n'||expressionChars[i+2]=='g')){
                     functionResult = executeFunction(expression.substring(i+1,i+3+handleLength(expression.substring(i+3))),isRadians);
                  }else{
                     int firstOffset = handleLength(expression.substring(i+4));
                     functionResult = executeFunction(expression.substring(i+1,i+firstOffset+4+handleLength(expression.substring(i+firstOffset+4))),isRadians);
                  }
                  result = computeValue('^',result,functionResult.get("result"));
                  i += functionResult.get("index");
                  break;
               }
               int nextOperatorIndex5 = i+1+handleLength(expression.substring(i+1));
               if(expressionChars[i+1]=='('){
                  Map<String,Double> bracketResult = computeBracket(expression.substring(i+1,nextOperatorIndex5),isRadians);
                  result = computeValue('^',result,bracketResult.get("result"));
                  i += bracketResult.get("index");
               }else{
                  Double tar5 = executeExpression(expression.substring(i+1,nextOperatorIndex5),isRadians);
                  result = computeValue('^',result,tar5);
                  i += expression.substring(i+1,nextOperatorIndex5).length();
               }
               if(isFirstCharacter){
                  i--;
                  isFirstCharacter = false;
               }
               break;
            case '(':
               if(expressionChars[i+1]=='s'||expressionChars[i+1]=='c'||expressionChars[i+1]=='t'||expressionChars[i+1]=='l'){
                  Map<String,Double> functionResult;
                  if(expressionChars[i+1]=='l'&&(expressionChars[i+2]=='n'||expressionChars[i+2]=='g')){
                     functionResult = executeFunction(expression.substring(i+1,i+3+handleLength(expression.substring(i+3))),isRadians);
                  }else{
                     int firstOffset = handleLength(expression.substring(i+4));
                     functionResult = executeFunction(expression.substring(i+1,i+firstOffset+4+handleLength(expression.substring(i+firstOffset+4))),isRadians);
                  }
                  result += functionResult.get("result");
                  i += 2+functionResult.get("index");
                  break;
               }
               Map<String,Double> bracketResult = computeBracket(expression.substring(i),isRadians);
               result += bracketResult.get("result");
               i += bracketResult.get("index");
               if(isFirstCharacter){
                  isFirstCharacter = false;
               }
               break;
         }
      }
      return result;
   }

   /**
    * 执行三角函数运算、对数运算、指数运算
    * @param function
    * @param isRadians
    * @return
    */
   public static Map<String,Double> executeFunction(String function,boolean isRadians) throws Exception{
      Map<String,Double> result = Maps.newHashMap();
      switch(function.charAt(0)){
         case 's':
            if(function.startsWith("sin")){
               int offset1 = handleLength(function.substring(3));
               double result1 = sin(executeExpression(function.substring(3,3+offset1),isRadians),isRadians);
               result.put("result",result1);
               result.put("index",3+new Double(offset1));
               return result;
            }
            if(function.startsWith("sec")){
               int offset2 = handleLength(function.substring(3));
               double result2 = sec(executeExpression(function.substring(3,3+offset2),isRadians),isRadians);
               result.put("result",result2);
               result.put("index",3+new Double(offset2));
               return result;
            }
            break;
         case 'c':
            if(function.startsWith("cos")){
               double result3 = cos(executeExpression(function.substring(3),isRadians),isRadians);
               int offset3 = handleLength(function.substring(3));
               result.put("result",result3);
               result.put("index",3+new Double(offset3));
               return result;
            }
            if(function.startsWith("cot")){
               int offset4 = handleLength(function.substring(3));
               double result4 = cot(executeExpression(function.substring(3,3+offset4),isRadians),isRadians);
               result.put("result",result4);
               result.put("index",3+new Double(offset4));
               return result;
            }
            if(function.startsWith("csc")){
               double result5 = csc(executeExpression(function.substring(3),isRadians),isRadians);
               int offset5 = handleLength(function.substring(3));
               result.put("result",result5);
               result.put("index",3+new Double(offset5));
               return result;
            }
            break;
         case 't':
            double result6 = tan(executeExpression(function.substring(3),isRadians),isRadians);
            int offset6 = handleLength(function.substring(3));
            result.put("result",result6);
            result.put("index",3+new Double(offset6));
            return result;
         case 'l':
            if(function.startsWith("ln")){
               double result7 = logarithm(e,executeExpression(function.substring(2),isRadians));
               int offset7 = handleLength(function.substring(2));
               result.put("result",result7);
               result.put("index",2+new Double(offset7));
               return result;
            }
            if(function.startsWith("lg")){
               double result8 = logarithm(10,executeExpression(function.substring(2),isRadians));
               int offset8 = handleLength(function.substring(2));
               result.put("result",result8);
               result.put("index",2+new Double(offset8));
               return result;
            }
            if(function.startsWith("log")){
               int firstIndex = nextOperatorIndex(function.toCharArray(),3);
               Double firstOffset = Double.parseDouble(function.substring(3,nextOperatorIndex(function.toCharArray(),3)));
               Double value = executeExpression(function.substring(firstIndex, firstIndex+handleLength(function.substring(firstIndex))),isRadians);
               double result9 = logarithm(firstOffset,value);
               double offset9 = handleLength(function.substring(firstIndex));
               result.put("result",result9);
               result.put("index",firstIndex+offset9-1);
               return result;
            }
            break;
      }
      return result;
   }

   public static double plus(double src,double tar){
      return src+tar;
   }

   public static double minus(double src,double tar){
      return src-tar;
   }

   public static double multiply(double src,double tar){
      return src*tar;
   }

   public static double divide(double src,double tar){
      if(tar!=0.0d){
         return src/tar;
      }else{
         return Double.NaN;
      }
   }

   public static double sin(double x,boolean isRadians){
      if(isRadians){
         return Math.sin(x);
      }else{
         return Math.sin(x*pi/180);
      }
   }

   public static double cos(double x,boolean isRadians){
      if(isRadians){
         return Math.cos(x);
      }else{
         return Math.cos(x*pi/180);
      }
   }

   public static double tan(double x,boolean isRadians){
      if(isRadians){
         return Math.tan(x);
      }else{
         return Math.tan(x*pi/180);
      }
   }

   public static double cot(double x,boolean isRadians){
      double tanx = tan(x,isRadians);
      if(tanx==0){
         return Double.NaN;
      }else{
         return 1/tanx;
      }
   }

   public static double sec(double x,boolean isRadians){
      double cosx = cos(x,isRadians);
      if(cosx==0){
         return Double.NaN;
      }else{
         return 1/cosx;
      }
   }

   public static double csc(double x,boolean isRadians){
      double sinx = sin(x,isRadians);
      if(sinx==0){
         return Double.NaN;
      }else{
         return 1/sinx;
      }
   }

   /**
    * 对数运算,如输入(10,100)则实际运算为log10(100)
    * @param offset 下标
    * @param value     * @return
    */
   public static double logarithm(double offset,double value){
      if(offset==0||value==0||Math.log(offset)==0){
         return Double.NaN;
      }
      return Math.log(value)/Math.log(offset);
   }

   /**
    * 指数运算,如输入(2,4)则实际运算为2^4
    * @param value     * @param index 指数
    * @return
    */
   public static double exponent(double value,double index){
      return Math.pow(value,index);
   }

   /**
    * 运算一个完整的括号的表达式,括号数量不对将扔出异常。
    * 主要用于将类似(1+(2+3))之类的表达式解析成1+(2+3)
    * @param expression 表达式
    * @param isRadians 是否是弧度
    * @return
    */
   public static Map<String,Double> computeBracket(String expression,boolean isRadians) throws Exception{
      Integer backIndex = expression.indexOf(")",1);
      int offset = 0;
      for(String frontExp = expression;;){
         int frontIndex = frontExp.indexOf("(",1);
         if(backIndex==-1){
            //TODO 扔出异常
            throw new Exception("错了");
         }
         //如果没有下一个"("或者下一个"("的位置在当前下一个")"位置之后
         if(frontIndex == -1||frontIndex>backIndex){
            double expressionResult = executeExpression(expression.substring(1,backIndex+offset),isRadians);
            Map<String,Double> result = Maps.newHashMap();
            result.put("result",expressionResult);
            result.put("index",Double.parseDouble(backIndex.toString())+offset);
            return result;
         }
         else{
            frontExp = frontExp.substring(frontIndex,frontExp.length());
            backIndex = frontExp.indexOf(")",backIndex+1-frontIndex);
            offset+=frontIndex;
         }
      }
   }

   public static boolean compareCharCount(String front,String back,String expression){
      int frontCount = 0,backCount = 0;
      for(String frontExp = expression;frontExp.indexOf(front) != -1;){
         int index = frontExp.indexOf(front);
         if(index == -1){
            break;
         }
         frontCount++;
         frontExp = frontExp.substring(index+front.length(),frontExp.length());
      }
      for(String backExp = expression;backExp.indexOf(back) != -1;){
         int index = backExp.indexOf(back);
         if(index == -1){
            break;
         }
         backCount++;
         backExp = backExp.substring(index+back.length(),backExp.length());
      }
      return frontCount==backCount;
   }

   /**
    * 获取表达式指定位置之后的一个运算符的位置
    * @param chars
    * @return
    */
   private static int nextOperatorIndex(char[] chars,int index){
      String expression = new String(chars);
      if(StringUtils.isEmpty(expression)){
         return 0;
      }
      int operatorIndex = index+1;
      for(;operatorIndex<expression.length();operatorIndex++){
         if(!stringIsNumeric(expression.substring(index,operatorIndex))&&chars[operatorIndex-1]!=('.')){
            String string = expression.substring(operatorIndex-1,operatorIndex);
            return operatorIndex-1;
         }
      }
      return operatorIndex;
   }

   /**
    * 获取表达式指定位置之前的一个运算符的位置
    */
   private static int previousOperatorIndex(char[] chars,int index){
      String expression = new String(chars,0,index);
      int operatorIndex = 1,frontBracket = 0,backBracket = 0;
      for(;operatorIndex<expression.length();operatorIndex++){

         if(!stringIsNumeric(expression.substring(expression.length()-operatorIndex))
               &&chars[expression.length()-operatorIndex]!='.'){
            return expression.length()-operatorIndex+1;
         }
      }
      return expression.length()-operatorIndex;
   }

   /**
    * 支持浮点型的判断字符串为数字类型的方法
    * @param cs
    * @return
    */
   public static boolean stringIsNumeric(final String cs){
      if(StringUtils.isEmpty(cs)){
         return false;
      }
      if(cs.startsWith(".")||cs.startsWith(".")){
         return false;
      }
      int pointCount = 0,size = cs.length();
      for(int i=0;i<size;i++){
         if(Character.isDigit(cs.charAt(i))==true){
            continue;
         }else if(cs.charAt(i)=='.'){
            if(pointCount>1){
               return false;
            }
            pointCount++;
         }else{
            return false;
         }
      }
      return true;
   }

   /**
    * 针对有双参数的操作,如乘、除、指数
    * @param operator
    * @param result
    * @param changedValue
    * @return
    */
   private static double computeValue(char operator,double result,double changedValue){
      Double result1 = 0D;
      switch(operator){
         case '*':result1 = multiply(result,changedValue);break;
         case '/':result1 = divide(result,changedValue);break;
         case '^':result1 = exponent(result,changedValue);break;
      }
      return result1;
   }

   /**
    * 针对执行函数,处理长度问题
    */
   private static int handleLength(String expression){
      if(expression.startsWith("(")){
         Integer backIndex = expression.indexOf(")",1);
         int offset = 0;
         for(String frontExp = expression;;) {
            int frontIndex = frontExp.indexOf("(", 1);
            if (backIndex == -1) {
               //TODO 扔出异常
            }
            //如果没有下一个"("或者下一个"("的位置在当前下一个")"位置之后
            if (frontIndex == -1 || frontIndex > backIndex) {
               return backIndex+offset+1;
            } else {
               frontExp = frontExp.substring(frontIndex, frontExp.length());
               backIndex = frontExp.indexOf(")", backIndex + 1 - frontIndex);
               offset += frontIndex;
            }
         }
      }else{
         return nextOperatorIndex(expression.toCharArray(),0);
      }
   }

}

 

类核心方法:executeExpression(),用于执行一个表达式

类常用方法:executeFunction(),用于执行三角函数、指数函数、对数函数

handleLength(),用于获取在指定运算符之后的参数的长度

computeBracket(),计算一串表达式中一个"("所匹配的")"的位置

stringIsNumeric(),比对字符串是否可转换为数字类型,与org.apache.commons.lang3.StringUtils的isNumeric()方法最大的差别是实现了对浮点类型的判断


实现思想:通过从左向右匹配操作符,对有优先级的方法使用括号进行提取运算,对乘除法,指数运算等采用将前一结果与后一参数直接运算的方式,因此一定要合理运用括号。匹配到操作符后,匹配对应参数位置和参数长度,提取进行运算后将结果汇总。


其他:除了括号外没有加入其他优先级判断,有兴趣可以自己实现


使用示例:

Double result = MathUtil.computeDefiniteIntegration(function,10,20,20000,false);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值