【问题标题】:Convert number in a certain base to a decimal number using recursion使用递归将某个基数的数字转换为十进制数
【发布时间】:2016-01-07 19:51:04
【问题描述】:

我的任务是创建一个递归方法 makeDecimal,当传递一个数字(由字符串表示)及其基数时,将数字转换为基数 10。您将需要使用方法 Integer.parseInt(str)。 (提示:使用子字符串。)此方法采用String 并返回它的整数形式。

例如,Integer.parseInt("21"); 将返回 int 21。

以下是 makeDecimal 工作原理的一些示例:

makeDecimal("11", 2) 将返回 3。

makeDecimal("100", 4) 将返回 16。

这是我的尝试:

public static double makeDecimal(String number, int base){
    int len = number.length();
    double f = 0;

    if(len <= 0)
        return 0;
    else{
        makeDecimal(number,base);

        double temp = Integer.parseInt(number.substring(len - 1, len + 1));
        f = f + temp * Math.pow(3, len-1);
    }

    len--;
    return f;
}

但是,我得到一个“溢出错误”,我什至不知道它是否写得正确。

【问题讨论】:

  • makeDecimal(number,base); - 这使得递归调用使用与以前完全相同的参数,不变。这意味着它永远不会结束,因为它总是以相同的状态进入方法。您需要确保在调用时传递不同的值。

标签: java eclipse recursion


【解决方案1】:

您正在使用与传入的完全相同的参数进行递归。因此,调用本身将以相同的方式递归,直到堆栈溢出。这不是递归应该如何工作的。相反,您需要弄清楚如何在当前调用中解决一个问题,然后递归解决一个较小的问题。

在您的代码中,甚至不清楚您使用的是什么逻辑。 (计算 3len-1 有什么意义?)试试这个:

  • 如果输入字符串的长度为 0,则答案为 0(您猜对了那部分)
  • 否则,取最后一个数字并在当前基数中解析它。那么答案就是这个值加上base 乘以所有的值,直到但不包括输入的最后一位数字。 (提示:这是使用递归的好地方。)

您应该能够将该描述翻译成适当的方法调用和substring() 的使用。

哦,还有一件事:这里没有理由使用double 值。始终坚持使用int 变量。你不需要Math.pow()

【讨论】:

  • 如果基数像 16 怎么办?
  • @nbro - 然后你解析最后一个数字基数 16。关键是最后一个数字是一个字符串,并且你得到了用于解析它的基数。我们的想法是始终使用Integer.parseInt() 的两个参数版本并使用给定的任何基础。
【解决方案2】:

这是使用递归的最简单解决方案,substringInteger.parseInt

public int makeDecimal(String value, int base) {
    // exit from recursion
    if (value.length() < 1) 
        return 0;

    //parsing last character of string
    int charValue = Integer.parseInt(value.substring(value.length() - 1), base);

    //calling recursion for string without last character
    return makeDecimal(value.substring(0, value.length() - 1), base) * base + charValue;
} 

【讨论】:

  • 这是一个很好的解决方案,因为您不需要创建例如我创建的表,因为我不知道(或者我没有想到)您可以使用parseInt 方法。无论如何,你不需要result,你可以简单地返回0。事实上,你没有在任何地方修改它:你使用它的唯一地方是如果长度小于1,即它是0(它可以在这种情况下无论如何都不会)。
  • @nbro 这是最简单的解决方案,但如果您手动解析它(使用创建的表或不使用它),您将获得更多控制权,并且能够使用例如 base 超过 36 和其他一些字符进行解析
  • 是的,这也是真的。
  • @nbro 是的,你是对的,结果完全没用,它是从以前的变体留下的,感谢您指出它
【解决方案3】:

这是我用Python编写原型后的解决方案(如果你有兴趣,我也可以附上Python源代码):

import java.util.HashMap;
import java.util.Map;

public class MakeDecimal {

    public static final Map<Character, Integer> alphabet = buildAlphabetTable();

    public static void main(String[] args) {

        // System.out.println(alphabet);
        System.out.println(makeDecimal("af10bb1", 16));
    }

    // pos refers to the position of the character in the string.
    // For example, if you have the following binary string 100
    // then 1 at the left is at position 2, 
    // the 0 in the middle is at position 1,
    // and the right most 0 is at position 0 
    // (you start counting from the right side).
    // In general, you would convert that string in the following way:
    // 2^2 * 1 + 2^1 * 0 + 2^0 * 0 = 4

    // If the base was n, then you would have
    // first * n^{pos + "length of string - 1"} + ... + penultimate * n^{pos + 1} + last * n^{pos}
    // Note that pos starts from 0.
    private static int makeDecimal(String s, int base, int pos) {
        if (s.length() == 0) {
            return 0;
        } else {
            int last = (int) Math.pow(base, pos) * alphabet.get(s.charAt(s.length() - 1));
            return makeDecimal(s.substring(0, s.length() - 1), base, pos + 1) + last;
        }
    }

    public static int makeDecimal(String s, int base) {
        if (s.length() == 0) {
            return 0;
        }

        if (base < 2 || base > 36) {
            throw new IllegalArgumentException("not base >= 2 and base <= 36");
        }

        return makeDecimal(s.toLowerCase(), base, 0);
    }


    // Creates a table that maps characters to their decimal value
    // the characters can be also '0' or '2' (or any other character number)
    // or they can be a character of the English alphabet
    private static Map<Character, Integer> buildAlphabetTable() {
        Map<Character, Integer> a = new HashMap<>(36);

        int i = 0;

        for (char c = '0'; c <= '9'; c++, i++) {
            a.put(c, i);
        }

        for (char c = 'a'; c <= 'z'; c++, i++) {
            a.put(c, i);
        }

        return a;
    }

}

我的解决方案基于以下帖子,您绝对应该阅读该帖子以刷新您对如何在基础之间转换的想法。

http://www.purplemath.com/modules/numbbase.htm

它不接受小于 2 或大于 36 的基数。当您传递大写英文字符时,它也会处理。

【讨论】:

    【解决方案4】:

    编辑: 起初,我混淆了此解决方案必须使用递归,因此没有它的我的原始答案可以是以下四个。

    这是带有递归但没有substringMath.pow 的解决方案:

    public double makeDecimal(String value, int base) {
        makeDecimal(value, base, value.length() - 1);
    }
    
    public double makeDecimal(String value, int base, int index) {
        double result = 0;
    
        if (index < 0)
            return result;
    
        double charValue = 0;
        char currentChar =  values.get(Character.toUpperCase(value.charAt(index));
        if (currentChar >= 0 && currentChar <= '9') {
           charValue = currentChar - '0';
        } else if (currentChar >= 'A' && currentChar <= 'Z') {
            charValue = currentChar - 'A';
        } else {
            throw new InvalidValueException("Unsupported character '" + currentChar + "'.");
        }
    
        if (charValue >= base) {
            throw new InvalidValueException("Wrong character '" + currentChar + "' for base '" base + "'.");
        }
    
        return makeDecimal(value, base, index - 1)) * base + charValue;
    } 
    

    原答案:这样的事情应该适用于从 2 到 36 的任何基地:

    private Map<Character, Integer> getCharValues(int base)
    {
        Map<Character, Integer> values = new HashMap<Character, Integer>();
        for (int i = 0; i < base; i++){
            if (i < 10) {
                values.put('0' + i, i);
            } else if (i < 36) {
                values.put('A' + i - 10, i);
            } else {
                throw new InvalidValueException("Base '" + base + "' not supported");
            }
        }
        return values;
    }
    
    public double makeDecimal(String value, int base)
    {
        Map<Character, Integer> charValues = getCharValues(base);
        double result = 0;
        for (int i = 0; i < value.length(); i++){
            result = result * base + charValues.get(Character.toUpperCase(Character.toUpperCase(value.charAt(i))));
        }
        return result;
    }
    

    如果您需要大于 36 的基数,您可以在方法 getCharValues 中扩展字符集。此外,最好不要每次都创建HasMap,而是将其存储为最大基数,如果 char 值超过给定的基数,则抛出异常。

    【讨论】:

    • 如果你想让它处理小写十六进制数字,你需要做一些工作。 (顺便说一句,不是反对者。)
    • 这不使用递归!由于我提到的原因,我是反对者。
    • @nbro 感谢指出,我完全错过了递归是有义务的
    猜你喜欢
    • 2015-06-14
    • 1970-01-01
    • 2013-09-23
    • 1970-01-01
    • 1970-01-01
    • 2014-07-14
    • 2014-12-14
    • 2013-02-02
    • 2012-04-19
    相关资源
    最近更新 更多