列举两个序列的交集

5
我有两个序列:
1. 无符号64位数k的倍数,例如k=5:{0, 5, 10, 15, 20, 25, ...} 2. 64位掩码m的子集加上64位常数C,例如m=0b1011:{C+0, C+1, C+2, C+3, C+8, C+9, C+10, C+11}。注意:C与m不相交,即(m & C) = 0。
除了逐个检查每个k的倍数并检查是否符合第二个序列的条件,或者检查掩码m的每个子集并与C进行OR运算,并检查是否能被k整除之外,还有其他方法可以枚举这两个序列的所有交集吗?
一个64位的数字P在这两个序列的交集中,如果P % k == 0,并且(P & C) == C,并且(P & ~(C | m)) = 0。
注意:枚举第二个序列实际上非常容易。
uint64_t m = ..., C = ...;
uint64_t subset = 0;
do {
    uint64_t value = subset + C;
    print(value);
    // if (value % 5)   => in the intersection of the sequences
    subset = (subset - m) & m;
} while (subset != 0);

编辑:枚举的顺序并不重要。
另外:我并不完全希望列举出所有的交集,但我想要一个可以始终前进并获取下一个交集的迭代器。

1
对于k == 2m == ~0,大约一半的64位数字在交集中。即使以某种方式发现下一个成员只需O(1)的时间,打印它们所有将需要几乎永远的时间。是否有关于km和/或C的限制,使得完整枚举合理;或者进一步处理结果以使完整枚举变得不必要?如果你不介意我问的话,这个练习的最终目标是什么? - Igor Tandetnik
1
对于 k==2m == ~0,大约一半的64位数字都在交集中。即使以某种方式仅需 O(1) 的时间来发现下一个成员,打印它们全部将需要几乎永远的时间。是否有关于 km 和/或 C 的限制,使得完全枚举变得合理;或者有进一步处理结果的方法,使得完全枚举变得不必要?如果你不介意我问的话,这个练习的最终目标是什么? - Igor Tandetnik
1
@IgorTandetnik 我的错 - 意图应该是 (subset - m) & m。我只是想构建一个迭代器,让我能够轻松地发现下一个交集,我并没有真正希望遍历所有的交集。 - Null
1
@IgorTandetnik 我的错 - 我的意图是 (subset - m) & m。我只想构建一个迭代器,让我能够轻松地发现下一个交集,我并没有真正希望遍历所有的交集。 - Null
@גלעדברקן 是的。都是完全任意的64位整数。 - Null
显示剩余29条评论
2个回答

1

C mod k 是一个常数,意味着我们正在寻找满足模 k 的子集 m ,并且只有一个值满足模 k —— 当它与 C mod k 相加后得到零模 k

x = (k - (C mod k)) mod k

每个位的m作为2的幂次方也是对k取模的常数。因此,任务似乎是枚举那些求和等于x mod k的组合。
例如,对于每个已经出现的组合,通过在m中添加当前的2的幂次方对k取模来生成一个新的组合。一个即时的优化可以是将在m中对k取模相等的2的幂次方分组。除此之外,我不知道有什么技巧可以加快这种搜索的速度 - 组合状态对k取模的数量可能取决于k和m两者。

-2
手头的问题是生成给定数字k的倍数,并且还满足由两个64位常量m和C定义的一些位操作条件。
如果我们从质因数的角度思考这个问题,那么我们要找的数字是那些具有k作为因子并且其位表示与给定掩码对齐的数字。如果我们有无限多个这样的数字,它们将均匀分布在数轴上,每个数字之间的间隔为k。
假设k=5,m=0b1011,C=0。那么k的倍数是{0, 5, 10, 15, 20, 25, ...},掩码m加上C的子集是{0, 1, 2, 3, 8, 9, 10, 11}。两个序列中共同存在的数字是{0, 5, 10}。
下面是一个提议的解决方案,它利用位操作和基本循环来逐步生成序列中的下一个数字。
class IntersectionGenerator {
private:
    uint64_t k, m, C, current_multiple, subset;
public:
    IntersectionGenerator(uint64_t k, uint64_t m, uint64_t C) : k(k), m(m), C(C) {
        subset = 0;
        current_multiple = 0;
    }

    // Return next intersection
    uint64_t next() {
        while (true) {
            uint64_t potential_value = subset + C;
            if ((potential_value % k) == 0) {
                subset = (subset - m) & m;
                return potential_value;
            }
            else {
                subset = (subset - m) & m;
                current_multiple += k;
            }
        }
    }
};

在这个实现中,IntersectionGenerator::next()会返回满足条件的下一个数字。当循环找到一个能被k整除且符合位运算条件的数字时,循环将继续执行。一旦找到这样的数字,它就会被返回,并计算m的下一个子集。如果数字不符合条件,则循环将继续到下一个子集,并增加当前k的倍数。
这个解决方案是基于您对类似迭代器的结构的要求,使用了逐步计算两个序列交集的方法。如果k相对于m的子集大小较小,这种方法将高效工作。
如果m的子集数量较大,这种方法将变得低效,因为它需要对所有k的倍数进行测试,以确定是否与m的所有子集有交集。一个包含n个元素的集合有2^n个子集,所以随着m的增大,寻找交集所需的迭代次数也会增加。
如果序列非常大,更高效的解决方案是预先计算并存储序列,然后应用集合交集算法来找到共同的元素。然而,这将需要大量的内存,并且无法提供您所需的“迭代器”样式界面。

1
差不多一个月前,我曾评论过你发了5个可能是AI回答,并让你了解到这方面的规定。从那时起,似乎你又发布了另外5个看起来很可能是由AI(比如ChatGPT)生成的回答(完全或部分)。我继续鼓励你自愿删除任何AI辅助的回答。 - NotTheDr01ds
1
读者应该仔细而批判地审查这个答案,因为由人工智能生成的信息往往包含基本错误和错误信息。如果您发现质量问题和/或有理由相信这个答案是由人工智能生成的,请相应地提供反馈。 - NotTheDr01ds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接