在 Matlab 3D 数组中查找重复的 2D 数组

Emi*_*son 4 algorithm hash matlab dictionary

worldStates 是一个 Matlab MxNxL 3D 数组(张量),包含一个 MxN 二进制值网格的 L 个状态。

ps 是与不同状态相关联的概率的长度 L 列表。

该函数[worldStates, ps] = StateMerge(worldStates, ps)应删除重复的世界状态,并将合并状态的概率与剩余的单个状态相加。重复状态是具有完全相同的二进制值配置的状态。

这是此功能的当前实现:

function [worldStates, ps] = StateMerge(worldStates, ps)
    
    M = containers.Map;
    
    for i = 1:length(ps)
        s = worldStates(:,:,i); 
        s = mat2str(s);
        if isKey(M, s)
            M(s) = M(s) + ps(i);
        else
            M(s) = ps(i);
        end
    end
    
    stringStates = keys(M);
    n = length(stringStates);
    
    sz = size(worldStates);
    worldStates = zeros([sz(1:2), n]);
    ps = zeros(1, 1, n);
    
    for i = 1:n
        worldStates(:,:,i) = eval(stringStates{i});
        ps(i) = M(stringStates{i});
    end
end 
Run Code Online (Sandbox Code Playgroud)

它使用 Map 能够在 O(L) 时间内删除重复项,使用状态作为键,使用概率作为值。由于 Matlab 映射不允许将通用数据结构作为键,因此状态被转换为字符串表示以用作键,然后使用 eval 函数转换回数组。

事实证明,这段代码可以满足我的需求,因为我想多次(10 ^ 3)处理许多状态(幅度~10 ^ 6)。问题在于将矩阵转换为字符串,这需要花费大量时间并且随着状态大小的缩放很差。下面给出了小 25x25 状态的示例:

在此处输入图片说明

如何以更有效的方式创建密钥?除了使用会产生更好结果的地图之外,还有其他解决方案吗?

编辑:按要求运行的代码。这个例子使得合并的可能性很小:

worldStates = double(rand(25,25, 1000) > 0.5);

weights = rand(1,1, 1000);
ps = weights./sum(weights);

[worldStates, ps] = StateMerge(worldStates, ps);
Run Code Online (Sandbox Code Playgroud)

在这个例子中会有很多合并:

worldStates = double(rand(25,25) > 0.5) .* ones(1,1,1000);
worldStates(1:2,1:2,:) = rand(2,2,1000) > 0.5;

weights = rand(1,1, 1000);
ps = weights./sum(weights);

[worldStates, ps] = StateMerge(worldStates, ps);
Run Code Online (Sandbox Code Playgroud)

rah*_*ma1 6

使用unique提取独特(合并)状态和accumarray总结的概率合并状态。请注意,此解决方案与您的解决方案一样,不会保留原始状态的顺序。正如@Wolfie 在评论中所建议的,您可以使用unique'stable' 选项来保留状态的顺序:

function [worldStates, ps] = StateMerge(worldStates, ps)
    [M, N, L] = size (worldStates);
    worldStates1 = reshape(worldStates, M*N, L).';
    [~, uc, ui] = unique(worldStates1, 'rows');
    ps = accumarray(ui, ps(:));
    worldStates = worldStates (:, :, uc);
end
Run Code Online (Sandbox Code Playgroud)

  • 作为参考,在 L = 1000 的测试中,该函数的速度快了 10 倍,并且对于较大的 L,其积极效果似乎更高。 (2认同)