【问题标题】:Javascript recursion for money laundering detection用于洗钱检测的 Javascript 递归
【发布时间】:2018-03-10 21:00:09
【问题描述】:

我正在执行一项统计分析,以确定是否有可能通过在特定时间范围内将较大的交易拆分为较小的交易来隐藏该交易。我正在做的是将较大的数据集分解为较小的子集(目前为 12 个数组),然后在每个子集上运行一系列循环,以确定元素的任何组合是否加起来在目标范围内。

这是我当前的代码:

amounts_matrix = [1380.54,9583.33,37993.04,3240.96...]


matrix_amounts = amounts_matrix.length
total_permutations = 0;
total_hits = 0;
target_range = 1
target = 130000
low_threshold = target - target_range
high_threshold = target + target_range
entries = []
range = 12


for(x = 0; x< matrix_amounts-(range-1); x++){
    amounts = amounts_matrix.slice(x, x+range)
    total_amounts = range


    for(i = 0; i< total_amounts; i++){
        entries.push(amounts[i])
        totalcheck(entries)
        entries = []
    }

    for(i = 0; i< total_amounts; i++){
        for(j = i+1; j< total_amounts; j++){
            entries.push(amounts[i])
            entries.push(amounts[j])
            totalcheck(entries)
            entries = []
        }
    }

    ...

    for(i = 0; i< total_amounts; i++){
        for(j = i+1; j< total_amounts; j++){
            for(k = j+1; k< total_amounts; k++){
                for(l = k+1; l< total_amounts; l++){
                    for(m = l+1; m< total_amounts; m++){
                        for(n = m+1; n< total_amounts; n++){
                            for(o = n+1; o< total_amounts; o++){
                                for(p = o+1; p< total_amounts;p++){
                                    for(q = p+1; q< total_amounts;q++){
                                        for(r = q+1; r< total_amounts;r++){
                                            for(s = r+1; s< total_amounts;s++){
                                                for(t = s+1; t< total_amounts;t++){
                                                    entries.push(amounts[i])
                                                    entries.push(amounts[j])
                                                    entries.push(amounts[k])
                                                    entries.push(amounts[l])
                                                    entries.push(amounts[m])
                                                    entries.push(amounts[n])
                                                    entries.push(amounts[o])
                                                    entries.push(amounts[p])
                                                    entries.push(amounts[q])
                                                    entries.push(amounts[r])
                                                    entries.push(amounts[s])
                                                    entries.push(amounts[t])
                                                    totalcheck(entries)
                                                    entries = []
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }


}


function totalcheck(array){ 

    total_permutations += 1;

    sum_amount = 0
    for(z = 0; z < array.length; z++){
        sum_amount += array[z]
    }

    if(sum_amount > low_threshold && sum_amount < high_threshold){
        total_hits += 1
        console.log(array)
        console.log(sum_amount.toFixed(2))
        console.log("---------------")
    }

}

console.log("overall total hits = " + total_hits)
console.log("overall total permutations = " + total_permutations)

我对那些 for 循环的广泛性感到非常尴尬,我想用一个函数来概括它,我可以告诉它运行 X 循环,而不是像这样构建它们。我发现的置换函数对我来说并不可行,因为它们都构建了充满全部可能性的数组;在我的工作中,我想检查目标,以避免出现巨大的数组并遇到内存问题。如何构建一个递归循环来做到这一点?

【问题讨论】:

  • 使用内存有什么问题?我读了你的问题,我想到的第一件事是“建立一系列总的可能性”。那是问题吗?为什么?
  • 你 c̶a̶n̶n̶o̶t̶ 不能合理地创建一个函数来创建循环,eval() 函数可以帮助你,但是,一个操纵它运行的循环数的函数可以很容易地完成,并且回调甚至可以提供给每个循环独特的功能
  • 因为我希望能够使用比 12 更大的窗口来完成它 - 这将使我进入 50 个数组的领域!长甚至更大。
  • 你能解释一下这应该怎么做吗?我想有一个更短+更好的解决方案
  • 我知道 'amounts_matrix' 无法重新组织,但 'amounts' 是您执行循环的子采样数组,除非我弄错了。这就是我建议您订购的东西,它会大大提高效率,并包含逻辑。

标签: javascript recursion accounting


【解决方案1】:

您可以构建要检查的索引列表:

 const positions = Array.from({length: 12}, (_, i) => i);

现在我们需要取最高的索引,增加它,当我们到达数组的上边界时,我们增加第二高的索引,以此类推,所以我们慢慢地遍历所有组合:

 function next(){
   for(let i = positions.length - 1; i >= 0; i--){
      if(positions[i] < amounts.length){
        positions[i]++;
        return true;
      }
      if(i == 0) return false;
      positions[i] = positions[i - 1] + 2;
   }
 }

如果这看起来很诡异,try it here

现在我们已经获得了索引,我们只需要将它们引用的数组值求和,直到找到目标:

  do {
    const sum = positions.reduce((sum, pos) => sum + amounts[pos], 0);
    if(sum === target) break;
  } while(next())

要获得所有不同长度的置换和,只需以不同的长度多次运行整个事情。

【讨论】:

  • 这看起来很有希望。让我想一想,但我认为它会成功。
  • 它变得很不稳定,并且似乎不在参数范围内(最终最后一列中的计数变得太高;应该最大为 10,但会上升到 11 和 12)。不过,我使用这种方法作为我回答的基础,所以谢谢!
【解决方案2】:

既然您已将问题标记并命名为“递归”,那么让我们构建一个递归。

我们还假设我们将提供函数排序输入,这样我们就可以避免所有 n 选择 k 个子集,以便在下一个数量太大时提前退出。 (如果输入没有排序,我们可以在下面的函数中简单地取消对“太大”的检查。)

(请注意,至少在浏览器中,JavaScript 提供有限的递归深度,因此您可以考虑将该过程转换为显式堆栈迭代。)

// Returns indexes of elements that compose sums within the specified range
function f(amounts, i, low, high, k, sum, indexes){
  if (!k)
    return low < sum && sum < high ? [indexes] : [];
    
  if (i == amounts.length || amounts.length - i < k)
    return [];
  
  if (sum + amounts[i + 1] > high)
    return low < sum ? [indexes] : [];

  let _indexes = indexes.slice();
  _indexes.push(i);

  return f(amounts, i + 1, low, high, k - 1, sum + amounts[i], _indexes)
           .concat(f(amounts, i + 1, low, high, k, sum, indexes));
}

console.log(JSON.stringify(f([1,2,3,4], 0, 6, 8, 3, 0, [])));
console.log(JSON.stringify(f([1,2,3,4], 0, 4, 7, 2, 0, [])));
console.log(JSON.stringify(f([1,2,3,4], 0, 4, 7, 3, 0, [])));

以上版本将搜索限制为特定数量的事务,k。我第一次发布的版本是一般k,意思是任何基数的子集:

function f(amounts, i, low, high, sum, indexes){
  if (i == amounts.length)
    return low < sum && sum < high ? [indexes] : [];
  
  if (sum + amounts[i + 1] > high)
    return low < sum ? [indexes] : [];

  let _indexes = indexes.slice();
  _indexes.push(i);
  
  return f(amounts, i + 1, low, high, sum + amounts[i], _indexes)
           .concat(f(amounts, i + 1, low, high, sum, indexes));
}

console.log(JSON.stringify(f([1,2,3,4], 0, 6, 8, 0, [])));
console.log(JSON.stringify(f([1,2,3,4], 0, 4, 7, 0, [])));

【讨论】:

    【解决方案3】:

    我最终使用了来自 Jonas W 的索引建议,这就是最终对我有用的方法。我可以通过更改 range_window 变量来更改窗口大小。

    const amounts_matrix = [1380.54,9583.33,37993.04,3240.96,9583.33,814.24,6000.00.....
    
    
    total_permutations = 0;
    total_hits = 0;
    target_range = 1
    target = 130000
    low_threshold = target - target_range
    high_threshold = target + target_range
    range_window = 12
    batch_max = 12
    
    
    for(x = 0; x < amounts_matrix.length-(range_window-1); x++){
        amounts = amounts_matrix.slice(x, x + range_window)
    
        for(batch_size = 0; batch_size <= batch_max; batch_size++){
    
            const positions = Array.from({length: batch_size}, (_, i) => i);
            //calculate the upper thresholds for each position
            var position_thresholds = new Array(batch_size)
            for(i = 0; i < positions.length; i++){
                position_thresholds[i] = i + amounts.length - positions.length
            }   
    
            var position = positions[positions.length-1];
    
            while(positions[0] < position_thresholds[position]){
                stormy_loop(positions, position)
            }
    
        }
    }
    
    
    function stormy_loop(positions, position){
        if(positions[position] <= position_thresholds[position]){
            totalcheck(positions)
            positions[position] += 1;
        }
        else{
            while(positions[position] > position_thresholds[position]){
                position -= 1
                positions[position] += 1;
            }
            cascade(positions,position)   
        }
    }
    
    function cascade(positions,position){
        base = positions[position]
        for(i = position + 1; i < positions.length; i++){
            position += 1
            base += 1
            positions[position] = base;
        }
    }
    
    
    function totalcheck(array){ 
    
        total_permutations += 1;
        output_array = []
    
        sum_amount = 0
        for(z = 0; z < array.length; z++){
            sum_amount += amounts[array[z]]
            output_array.push(amounts[array[z]])
        }
    
        if(sum_amount > low_threshold && sum_amount < high_threshold){
            total_hits += 1
            console.log(output_array)
            console.log(sum_amount.toFixed(2))
            console.log("total hits = " + total_hits)
            console.log("total permutations = " + total_permutations)
            console.log("---------------")
        }
    
    }
    

    【讨论】:

    • 我总是很高兴看到有人真正理解我的答案 :) 干得好!
    【解决方案4】:
     for(i = 0; i< total_amounts; i++){
            for(j = i+1; j< total_amounts; j++){
                for(k = j+1; k< total_amounts; k++){
                    for(l = k+1; l< total_amounts; l++){
                        for(m = l+1; m< total_amounts; m++){
                            for(n = m+1; n< total_amounts; n++){
                                for(o = n+1; o< total_amounts; o++){
                                    for(p = o+1; p< total_amounts;p++){
                                        for(q = p+1; q< total_amounts;q++){
                                            for(r = q+1; r< total_amounts;r++){
                                                for(s = r+1; s< total_amounts;s++){
                                                    for(t = s+1; t< total_amounts;t++){
                                                        entries.push(amounts[i])
                                                        entries.push(amounts[j])
                                                        entries.push(amounts[k])
                                                        entries.push(amounts[l])
                                                        entries.push(amounts[m])
                                                        entries.push(amounts[n])
                                                        entries.push(amounts[o])
                                                        entries.push(amounts[p])
                                                        entries.push(amounts[q])
                                                        entries.push(amounts[r])
                                                        entries.push(amounts[s])
                                                        entries.push(amounts[t])
                                                        totalcheck(entries)
                                                        entries = []
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    

    可以变成这样

    function loopinator(){
        for(i=0; i<total_amounts; i++){
            for(j=0; j<11; j++){//You had 11 loops plus root loop
                entries.push(amounts[( i+j )]);//Will increase with each iteration of j loop, simulating loop branching
            }
            //Will run at same time as it did before, after all nested roots, but before next iteration of i loop
            totalcheck(entries);
            entries = [];
    
        }
    }
    

    【讨论】:

    • 这似乎只针对直接发生的事情;如果我的基本集是 [1,2,3,4,5,6] 而我的目标是 9,它将捕获 [2,3,4] 和 [4,5] 但不是 [1,2,6] 和 [ 3,6].
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-12
    • 1970-01-01
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-11
    相关资源
    最近更新 更多