【发布时间】:2014-03-18 11:13:30
【问题描述】:
我试图在 Haskell 中解决一个难题并编写了以下代码:
u 0 p = 0.0
u 1 p = 1.0
u n p = 1.0 + minimum [((1.0-q)*(s k p)) + (u (n-k) p) | k <-[1..n], let q = (1.0-p)**(fromIntegral k)]
s 1 p = 0.0
s n p = 1.0 + minimum [((1.0-q)*(s (n-k) p)) + q*((s k p) + (u (n-k) p)) | k <-[1..(n-1)], let q = (1.0-(1.0-p)**(fromIntegral k))/(1.0-(1.0-p)**(fromIntegral n))]
不过,这段代码非常慢。我怀疑这样做的原因是同样的事情被一次又一次地计算出来。因此,我做了一个记忆版本:
memoUa = array (0,10000) ((0,0.0):(1,1.0):[(k,mua k) | k<- [2..10000]])
mua n = (1.0) + minimum [((1.0-q)*(memoSa ! k)) + (memoUa ! (n-k)) | k <-[1..n], let q = (1.0-0.02)**(fromIntegral k)]
memoSa = array (0,10000) ((0,0.0):(1,0.0):[(k,msa k) | k<- [2..10000]])
msa n = (1.0) + minimum [((1.0-q) * (memoSa ! (n-k))) + q*((memoSa ! k) + (memoUa ! (n-k))) | k <-[1..(n-1)], let q = (1.0-(1.0-0.02)**(fromIntegral k))/(1.0-(1.0-0.02)**(fromIntegral n))]
这似乎快了很多,但现在我遇到了内存不足的错误。我不明白为什么会发生这种情况(java中的相同策略,没有递归,没有问题)。有人可以为我指出如何改进此代码的正确方向吗?
编辑:我在这里添加我的 java 版本(因为我不知道该放在哪里)。我意识到代码对读者并不友好(没有有意义的名称等),但我希望它足够清晰。
public class Main {
public static double calc(double p) {
double[] u = new double[10001];
double[] s = new double[10001];
u[0] = 0.0;
u[1] = 1.0;
s[0] = 0.0;
s[1] = 0.0;
for (int n=2;n<10001;n++) {
double q = 1.0;
double denom = 1.0;
for (int k = 1; k <= n; k++ ) {
denom = denom * (1.0 - p);
}
denom = 1.0 - denom;
s[n] = (double) n;
u[n] = (double) n;
for (int k = 1; k <= n; k++ ) {
q = (1.0 - p) * q;
if (k<n) {
double qs = (1.0-q)/denom;
double bs = (1.0-qs)*s[n-k] + qs*(s[k]+ u[n-k]) + 1.0;
if (bs < s[n]) {
s[n] = bs;
}
}
double bu = (1.0-q)*s[k] + 1.0 + u[n-k];
if (bu < u[n]) {
u[n] = bu;
}
}
}
return u[10000];
}
public static void main(String[] args) {
double s = 0.0;
int i = 2;
//for (int i = 1; i<51; i++) {
s = s + calc(i*0.01);
//}
System.out.println("result = " + s);
}
}
【问题讨论】:
-
样本输入和预期输出会有所帮助。
-
和类型签名!
-
您是否尝试过使用
-O进行编译,或者您是否在 GHCi 中运行它? -
您能否将其分解为几个较小的表达式,例如在
where子句中,或者至少将其拆分为多行?这 190 个字符行很难阅读。 -
Reformatted code,这样可读性更高,但仍然需要类型签名
标签: performance haskell memoization