【问题标题】:Memoization of a function of various inputs in JavaJava中各种输入的函数的记忆
【发布时间】:2015-08-26 08:41:07
【问题描述】:

我有一个代价高昂的函数,将各种 double 变量作为输入:

public double f(double v1, double v2, double v3){
    ...
    return output;
}

所以我尝试使用两种不同的策略来记忆它的输出。

嵌套哈希映射:

Map<Double,Map<Double,Map<Double,Double>>> map = new HashMap<>();

public double f(double v1, double v2, double v3){
    // This is abbreviated: in my case I made sure to call only once each "get()"
    if(map.containsKey(v1) && map.get(v1).containsKey(v2) && map.get(v1).get(v2).containsKey(v3))
         return map.get(v1).get(v2).get(v3);
    ...
    // calculations
    ...
    // put "output" in the map (and create new intermediate HashMaps when needed)
    ...
    return output;
}

自定义HashMap键:

public class DoubleKey {
   public final double[] values;
   public DoubleKey(double[] values){ this.values = values;}

   @Override
   public boolean equals(Object key){
      if(key instanceof DoubleKey)
         return Arrays.equals(values, ((DoubleKey)key).values);
      return false;
   }

   @Override
   public int hashcode(){
      return Arrays.hashcode(values);
   }
}

Map<DoubleKey,Double> map = new HashMap<>();

public double f(double v1, double v2, double v3){
    DoubleKey key = new DoubleKey(new double[]{v1,v2,v3});
    if(map.containsKey(key))
         return map.get(key);
    ...
    // calculations
    ...
    map.put(key, output);
    return output;
}

现在,我预计第二种方法会更快,因为它使用单个 Hashmap,并且总体感觉更优雅。但事实证明,与第一种方法相比,我从第二种方法中获得的好处更少,后者产生了很大的速度提升。

您知道为什么第二种方法效率较低吗?这是使用Arrays.equals() 和/或Arrays.hashcode() 的成本吗?

更一般地说,您知道其他更有效的记忆技术吗?

【问题讨论】:

  • java.lang.Object 只计算一次哈希码并存储它。我认为您也应该使用这种方法。此外,添加三个双字段而不是数组更有效。
  • 您还可以使用比常规 Java 集合更有效的方法来存储来自 trove.starlight-systems.com 的 TObjectDoubleMap 等原始类型
  • 请定义高效
  • @OldCurmudgeon,我的意思是“快”
  • @sibnick,感谢您的提示!我尝试存储哈希码,但并没有做出太大的改变。

标签: java arrays hashmap memoization


【解决方案1】:

这两个解决方案不相等。第一个硬编码仅支持 3 个双参数,而第二个支持任意数量的参数。

如果你只需要支持 3 个参数,我认为保存 3 个实例变量更有效:

编辑: 在 cmets 之后,将使用 varargs ctor 的原始答案替换为显式的单个 args。可能更有效(不构造数组)。还使实例 vars final 以启用编译器优化

public class DoubleKey
{
    final double arg1, arg2, arg3;
    final int hashCode;

    public DoubleKey(double arg1, double arg2, double arg3)
    {
        this.arg1 = arg1;
        this.arg2 = arg2;
        this.arg3 = arg3;
        hashCode = Objects.hash(arg1, arg2, arg3);
    }

    @Override
    public boolean equals(Object key)
    {
        if (key instanceof DoubleKey) {
            DoubleKey dk = (DoubleKey) key;
            return arg1 == dk.arg1 && arg2 == dk.arg2 && arg3 == dk.arg3;
        }
        return false;
    }

    @Override
    public int hashCode()
    {
        return hashCode;
    }
}

现在已经开始构建 DoubleKey 实例了

DoubleKey key = new DoubleKey(v1,v2,v3);

【讨论】:

  • 为什么不public DoubleKey(double value1, double value2, double value3) {...}
  • 你将如何计算一个 hashCode?​​span>
  • 不知道commons HashCodeBuilder的效率如何,也不知道怎么用3个double值构造hash码。很高兴看到
  • 例如long tmp = Double.doubleToLongBits(v1) ^ Double.doubleToLongBits(v2) ^ Double.doubleToLongBits(v3); int hash = (int)((tmp&gt;&gt;&gt;32) ^tmp);
  • 公共 HashCodeBuilder 的效率 它成为问题的可能性很小。我想说担心常用 API 的效率是一种浪费,您可能正在研究功能。但是我会只使用manyexistingcachingsolutions之一,而不是自己滚动。 @Cached public Whatever boo(); 已排序。
猜你喜欢
  • 1970-01-01
  • 2014-07-20
  • 2020-11-07
  • 1970-01-01
  • 1970-01-01
  • 2022-12-17
  • 2012-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多