Java 将一个数组的每个元素乘以另一个数组的每个元素,然后对新的非常大的数组进行排序
免责声明 这是我的课程练习,不是正在进行的比赛 问题描述 问题描述非常直截了当:Java 将一个数组的每个元素乘以另一个数组的每个元素,然后对新的非常大的数组进行排序,java,c++,algorithm,sorting,Java,C++,Algorithm,Sorting,免责声明 这是我的课程练习,不是正在进行的比赛 问题描述 问题描述非常直截了当: 给出了两个数组A和B,分别包含n和m个元素。你需要排序的数字是Ai*Bj,因为1答案的线索在于你的观察 如果我们先对两个数组A和B排序,然后将它们相乘,我们得到 2 8 14 18 7 28 49 63 8 32 56 72 11 44 77 99这是一个带有m 已排序的子阵列 所以有n个数据序列被排序,问题是使用这些序列来生成答案 提示1:您可以使用优先级队列解决此问题吗。队列中元素的数量将与生成的已排序列表的数
给出了两个数组A和B,分别包含n和m个元素。你需要排序的数字是Ai*Bj,因为1答案的线索在于你的观察 如果我们先对两个数组A和B排序,然后将它们相乘,我们得到
2 8 14 18 7 28 49 63 8 32 56 72 11 44 77 99
这是一个带有m
已排序的子阵列
所以有n个数据序列被排序,问题是使用这些序列来生成答案
提示1:您可以使用优先级队列解决此问题吗。队列中元素的数量将与生成的已排序列表的数量相同
与
我测量了
double Faster(std::vector<int> lhs, std::vector<int> rhs)
{
Counter result;
if (lhs.size() == 0 || rhs.size() == 0) return 0;
std::sort(lhs.begin(), lhs.end());
std::sort(rhs.begin(), rhs.end());
if (lhs.size() < rhs.size()) {
std::swap(lhs, rhs);
}
size_t l = 0;
size_t r = 0;
size_t lhs_size = lhs.size();
size_t rhs_size = rhs.size();
std::priority_queue<Generator, std::vector< Generator >, MinHeap > queue;
for (size_t i = 0; i < lhs_size; i++) {
queue.push(Generator(i, 0, lhs[i] * rhs[0]));
}
Generator curr;
while (queue.size()) {
curr = queue.top();
queue.pop();
result.push_back(curr.product);
curr.i_rhs++;
if( curr.i_rhs < rhs_size ){
queue.push(Generator(curr.i_lhs, curr.i_rhs, lhs[curr.i_lhs] * rhs[curr.i_rhs]));
}
}
return result.sum();
}
速度加倍(std::vector lhs,std::vector rhs)
{
反结果;
if(lhs.size()==0 | | rhs.size()==0)返回0;
排序(lhs.begin(),lhs.end());
排序(rhs.begin(),rhs.end());
如果(lhs.size()queue;
对于(大小i=0;i
要比下面的简单实现更快
double Naive(std::vector<int> lhs, std::vector<int> rhs)
{
std::vector<int> result;
result.reserve(lhs.size() * rhs.size());
for (size_t i = 0; i < lhs.size(); i++) {
for (size_t j = 0; j < rhs.size(); j++) {
result.push_back(lhs[i] * rhs[j]);
}
}
std::sort(result.begin(), result.end());
Counter aCount;
for (size_t i = 0; i < result.size(); i++) {
aCount.push_back(result[i]);
}
return aCount.sum();
}
double-Naive(标准::向量lhs,标准::向量rhs)
{
std::向量结果;
结果.保留(lhs.size()*rhs.size());
对于(size_t i=0;i
排序输入向量比排序输出向量快得多。
我们为每一行创建一个生成器,它将遍历所有列。当前产品将作为优先级值添加到队列中,一旦生成了所有生成器,我们将从队列中读取它们
然后,如果每个生成器还剩下一列,我们将其添加回队列。这是从预先排序的输入的输出中有m个大小为n的子阵列的观察得出的。队列保存每个子数组的所有m个当前最小值,其中最小的一个是整个列表中剩余的最小值。当删除并重新添加生成器时,它将确保top
值是结果的下一个最小项
循环仍然是O(nm),因为每个生成器创建一次,读取的最小值是O(1),插入队列的值是O(logn)。我们每行做一次,所以O(nm*logn+nm)简化为O(nm logn)
原始解决方案是O(nm对数nm)
我从上面的解决方案中发现的性能瓶颈是插入队列的成本,在这方面我有一个性能提升,但我不认为它比算法
更快
合并O(mn)中所有这些已排序的子阵列
乘积<2^31,因此32位整数就足够了,基数排序基数为256就可以了。每10项的总和可能需要64位
更新-您在评论中没有提到256MB的内存限制,我只是注意到了这一点。输入数组大小为6000*6000*4=137.33MB。分配一个工作数组,其大小为原始数组的一半(四舍五入:工作数组大小=(1+原始数组大小)/2),最坏情况下,3000*6000个元素(<210MB所需总空间)。处理原始(产品)将数组分成两半,并使用基数排序对原始数组的两半进行排序。将已排序的下半部分移动到工作数组中,然后将工作数组与原始数组的上半部分合并回原始数组。在我的系统(英特尔3770K 3.5 ghz,Win 7 Pro 64位)上,两个基数排序将花费不到0.4秒的时间(每个大约0.185秒)和3000*6000个整数的一次合并大约需要0.16秒,排序部分不到0.6秒。使用这种方法,在执行乘法之前不需要对A或B排序
是否允许使用SIMD/xmm寄存器对A和B进行外积乘法(AO.x B)
基C++ 256基数排序的C++代码:< /P>
// a is input array, b is working array
uint32_t * RadixSort(uint32_t * a, uint32_t *b, size_t count)
{
size_t mIndex[4][256] = {0}; // count / index matrix
size_t i,j,m,n;
uint32_t u;
for(i = 0; i < count; i++){ // generate histograms
u = a[i];
for(j = 0; j < 4; j++){
mIndex[j][(size_t)(u & 0xff)]++;
u >>= 8;
}
}
for(j = 0; j < 4; j++){ // convert to indices
m = 0;
for(i = 0; i < 256; i++){
n = mIndex[j][i];
mIndex[j][i] = m;
m += n;
}
}
for(j = 0; j < 4; j++){ // radix sort
for(i = 0; i < count; i++){ // sort by current lsb
u = a[i];
m = (size_t)(u>>(j<<3))&0xff;
b[mIndex[j][m]++] = u;
}
std::swap(a, b); // swap ptrs
}
return(a);
}
//a是输入数组,b是工作数组
uint32_t*半径排序(uint32_t*a、uint32_t*b、大小计数)
{
size_t mIndex[4][256]={0};//计数/索引矩阵
尺寸i,j,m,n;
uint32_t u;
对于(i=0;i>=8;
}
}
对于(j=0;j<4;j++){//转换为索引
m=0;
对于(i=0;i<256;i++){
n=mIndex[j][i];
mIndex[j][i]=m;
m+=n;
}
}
对于(j=0;j<4;j++){//基数排序
对于(i=0;i m=(size_t)(u>>(j想不出任何好的解决方案来将所有这些已排序的子数组合并到O(n)
你想到的最好的方法是什么?(我想我问的是O(mn log(min(m,n))会做什么?)(没有被指定为非负的元素并不完全有帮助。)@greybeard它们都是非负的,你知道吗
// helper to catch every tenth element.
struct Counter {
int mCount;
double mSum;
Counter() : mCount(0), mSum(0) {}
void push_back(int val)
{
if (mCount++ % 10 == 0)
{
mSum += val;
}
}
double sum() { return mSum; }
};
// Storage in the priority queue for each of the sorted results.
struct Generator {
int i_lhs;
int i_rhs;
int product;
Generator() : i_lhs(0), i_rhs(0), product(0) {}
Generator(size_t lhs, size_t rhs, int p) : i_lhs(lhs), i_rhs(rhs), product(p)
{
}
};
// comparitor to get lowest value product from a priority_queue
struct MinHeap
{
bool operator()(const Generator & lhs, const Generator & rhs)
{
if (lhs.product > rhs.product) return true;
return false;
}
};
double Faster(std::vector<int> lhs, std::vector<int> rhs)
{
Counter result;
if (lhs.size() == 0 || rhs.size() == 0) return 0;
std::sort(lhs.begin(), lhs.end());
std::sort(rhs.begin(), rhs.end());
if (lhs.size() < rhs.size()) {
std::swap(lhs, rhs);
}
size_t l = 0;
size_t r = 0;
size_t lhs_size = lhs.size();
size_t rhs_size = rhs.size();
std::priority_queue<Generator, std::vector< Generator >, MinHeap > queue;
for (size_t i = 0; i < lhs_size; i++) {
queue.push(Generator(i, 0, lhs[i] * rhs[0]));
}
Generator curr;
while (queue.size()) {
curr = queue.top();
queue.pop();
result.push_back(curr.product);
curr.i_rhs++;
if( curr.i_rhs < rhs_size ){
queue.push(Generator(curr.i_lhs, curr.i_rhs, lhs[curr.i_lhs] * rhs[curr.i_rhs]));
}
}
return result.sum();
}
double Naive(std::vector<int> lhs, std::vector<int> rhs)
{
std::vector<int> result;
result.reserve(lhs.size() * rhs.size());
for (size_t i = 0; i < lhs.size(); i++) {
for (size_t j = 0; j < rhs.size(); j++) {
result.push_back(lhs[i] * rhs[j]);
}
}
std::sort(result.begin(), result.end());
Counter aCount;
for (size_t i = 0; i < result.size(); i++) {
aCount.push_back(result[i]);
}
return aCount.sum();
}
// a is input array, b is working array
uint32_t * RadixSort(uint32_t * a, uint32_t *b, size_t count)
{
size_t mIndex[4][256] = {0}; // count / index matrix
size_t i,j,m,n;
uint32_t u;
for(i = 0; i < count; i++){ // generate histograms
u = a[i];
for(j = 0; j < 4; j++){
mIndex[j][(size_t)(u & 0xff)]++;
u >>= 8;
}
}
for(j = 0; j < 4; j++){ // convert to indices
m = 0;
for(i = 0; i < 256; i++){
n = mIndex[j][i];
mIndex[j][i] = m;
m += n;
}
}
for(j = 0; j < 4; j++){ // radix sort
for(i = 0; i < count; i++){ // sort by current lsb
u = a[i];
m = (size_t)(u>>(j<<3))&0xff;
b[mIndex[j][m]++] = u;
}
std::swap(a, b); // swap ptrs
}
return(a);
}