我需要在平面文件读取循环中跟踪最近7天的工作时间。它被用来衡量工作名单的“疲劳性”。
现在我有一些有用的东西,但它似乎相当冗长,我不确定是否有一种更简洁的模式。
目前,我有一个带有静态数组的Java类来保存最后x天的数据,然后当我读完文件时,我切断了第一个元素并将其他6个元素(一周滚动总数)移回一。这个静态数组的处理是用它自己的方法完成的,即
/**
* Generic rolling average/total method. Keeps adding to an array of
* last 'x' seen.
* @param d Datum point you want to add/track.
* @param i Number of rolling periods to keep track of eg. 7 = last 7 days
* NOT USED AT MOMENT DURING TESTING
* @param initFlag A flag to initialize static data set back to empty.
* @return The rolling total for i periods.
*/
private double rollingTotal(double d, boolean initFlag) {
// Initialize running total array eg. for new Employyes
if (initFlag) {
runningTotal = null;
}
else {
// move d+1 back to d eg. element 6 becomes element 5
for (int x = 0; x< 6 ; x++) {
runningTotal[x] = runningTotal[x+1];
}
// Put current datum point at end of array.
runningTotal[6]= d;
}
// Always return sum of array when this method is called.
double myTotal = 0.0;
for (int x = 0; x<7; x++) {
myTotal+= runningTotal[x];
}
System.err.print(Arrays.toString(runningTotal)+ '\n' );
return myTotal;
}
我的问题:这是一种合理的设计方法,还是有一些令人眼花缭乱的明显和简单的任务? 谢谢你们
答案 0 :(得分:5)
这当然有效,但你做的工作比你要多一些。您可以避免移动所有数据,并且可以对其进行设置,以便计算下一个总数是减去最旧值并添加新值。
例如:
// assume that currentIndex is where you want to add the new item
// You have another value, currentTotal, that is initialized at 0.
currentTotal = currentTotal - runningTotal[currentIndex] + d;
runningTotal[currentIndex] = d;
// increment the index.
currentIndex = (currentIndex + 1) % 7;
这使用循环缓冲区并保留currentTotal
,以便它始终可用。
答案 1 :(得分:4)
我会说使用一个队列并推送新的并弹出旧的。为了跟踪平均值,您还可以从运行总计中减去弹出值并添加新值(您需要一个静态或实例变量或传递旧的总和)。无需访问其余元素。另外,如果initFlag为真,那么runningTotal在哪里被初始化?
private double rollingTotal(double d, boolean initFlag) {
if(initFlag) vals = new Queue<Integer>();
else {
if(vals.size() == 7) // replace 7 with i.
total -= vals.pop().intValue();
}
vals.push(d);
total += d;
}
return total;
}
我相信Queue是抽象的,所以你需要弄清楚要使用哪种实现。我建议使用基于链表的。
答案 2 :(得分:2)
您可以尝试使用循环缓冲区,而不是每次添加都移动所有数据:
runningTotal[nextIndex] = d;
nextIndex+=1;
if (nextIndex>=7) nextIndex = 0;
所以nextIndex
始终指向最早的基准面。你仍然可以像以前一样从头到尾求和。
答案 3 :(得分:2)
您可以使用exponential weighted moving average。它写得相当长,但相比之下,代码是微不足道的。它也倾向于给出更平滑的结果。
double previous;
static final double DAY = 1.0;
static final double WEEK = 6.0;
static final double ALPHA = DAY/WEEK;
private double movingAverage(double d) {
return previous = ALPHA * d + (1 - ALPHA) * previous ;
}
注意:这是公式的优化版本
double previous;
static final double DAY = 1.0;
static final double WEEK = 6.0;
static final double ALPHA = 1 - Math.exp(-DAY/WEEK);
private double movingAverage(double d) {
return previous = ALPHA * d + (1 - ALPHA) * previous ;
}
在这种情况下,后面的公式更准确,因为alpha不会改变Math.exp
的开销并不重要。如果alpha可以改变,并且通常很小,我建议使用第一个公式。
答案 4 :(得分:2)
使用ArrayList而不是数组会更容易。然后你可以使用
ArrayList<Double> runningTotal = new ArrayList<Double>();
....
runningTotal.remove(0);
runningTotal.add(d);
答案 5 :(得分:1)
为什么要将runningTotal
初始化为null?它的类型是什么?声明的地方?如果您放置一些类似于实际Java代码的代码示例,它会很好。
继续,我的批评将如下:你的功能做得太多了。功能或方法应该具有凝聚力。更恰当的是,他们应该做一件事,一件事。
更糟糕的是,当x = 5时,你的for循环会发生什么?您将runningTotal[6]
复制到runningTotal[5]
,但在第5和第6位有两个相同值的副本。
在您的设计中,您的功能
它做得太多了。
我的第一个建议是不要在阵列中移动东西。相反,实现circular buffer并使用它而不是数组。它将简化您的设计。我的第二个建议是将事情分解为具有凝聚力的功能:
这就是我要做的事情:)。
// java pseudocode below - might not compile.
// assume you have a class called CircularBuffer, of say, doubles,
public class CircularBuffer
{
public CircularBuffer(final int capacity) {...}
public int getSize(){ ... return # of elements in it ... }
public add(final Double d){ ... add to the end, drop from the front if we reach capacity... }
public Iterator<Double> iterator(){ ... gets an interator over the content of the buffer ...}
}
// somewhere else, in another class... NOT ON CircularBuffer
public class Calculator
{
//assume none of the double values is null
static public Double sum(final Double ... doubles )
{
double sum= 0;
for( Double d : doubles )
{
total += d.doubleValue();
}
return sum;
}
// you can calculate other things too
static public Double avg(final Double ... doubles ){...}
static public Double std(final Double ... doubles ){...}
}
/// somewhere else
{
CircularBuffer buffer = new CircularBuffer(7);
while( readingAndReadingAndReading )
{
// drops oldest values as it reaches capacity
// always keeping the latest 7 readings
buffer.add( getLatestValueFromSomewhere() );
}
System.out.println( "total=" + Calculator.sum() );
System.out.println( "average=" + Calculator.avg() );
System.out.println( "standard deviation=" + Calculator.std() );
}
答案 6 :(得分:0)
你的任务太简单了,你所采用的方法肯定对这份工作有好处。但是,如果你想使用更好的设计,你必须摆脱所有数字运动;你最好使用FIFO队列并充分利用push和pop方法;这样代码就不会反映任何数据移动,只是“新数据”和“删除超过7天的数据”的两个逻辑操作。