【问题标题】:Logic: Applying gravity to a vector逻辑:将重力应用于矢量
【发布时间】:2015-03-31 18:37:00
【问题描述】:

有一个方法叫gravity(Vector[] vector) 向量包含数字序列。重力函数应在应用重力后返回一个新向量,如下所述。

假设 0 是空气,1 是砖。当施加重力时,砖块应该会下降到最低水平。

设向量 = [3, 7, 8]

将其转换为二进制我们得到:
0 0 1 1 为 3
0 1 1 1 为 7
1 0 0 0 为 8

应用重力:
0 0 0 0 即 0
0 0 1 1 即 3
1 1 1 1 即 15

所以重力函数应该返回 [0, 3, 15]。

希望你们理解解释。我尝试了很多,但我无法弄清楚这其中的逻辑。我观察到的一件事是在施加重力之前和之后向量中的数字总和保持不变。

也就是说,
对于上述情况,3 + 7 + 8 = 18 = 0 + 3 + 15。

【问题讨论】:

  • 向量是否总是有 3 个元素,或者您正在寻找通用解决方案?
  • 其实一般的解决方案。
  • 您的问题到底是什么?想要重力函数的算法吗?或者您想知道为什么总和保持不变?
  • 我需要重力函数的算法。
  • 请看下面我的回答。我认为它易于理解,代码简单,并且性能也非常好(对于向量大小 N

标签: algorithm math logic


【解决方案1】:

我认为这就像计算每个位置的总“1”位一样简单......

N为输入向量大小,b为输入元素的最长二进制长度

  1. 预计算每个位置的'1'位的总数,存储在count[]中,O(N*b)
  2. 运行重力函数,即从count[]中重新生成N个数字,O(N*b)

总运行时间为 O(N*b)

以下是 C++ 中的示例代码

#include<bits/stdc++.h>
using namespace std;
int v[5] = {3,9,7,8,5};
int cnt[5] = {0};
vector<int> ans;

vector<int> gravity(){
	vector<int> ret;
	for(int i=0; i<5;i++){
		int s = 0;
		for(int j=0; j<5;j++)
			if(cnt[j]){
				s += (1<<j); cnt[j]--;
			}
		ret.push_back(s);
	}
	return ret;
}

int main(){
	
	// precompute sum of 1 of each bit
	for(int i=0, j=0, tmp=v[i]; i<5; i++, j=0, tmp=v[i]){
		while(tmp){
			if(tmp&1) cnt[j]++;
			tmp >>= 1; j++;
		}
	}
	
	ans = gravity();
	
	for(int i=ans.size()-1; i>=0; i--) printf("%d ", ans[i]);
	
	return 0;	
}

输出如下:

成功时间:0 内存:3272 信号:0

0 1 1 15 15

【讨论】:

    【解决方案2】:

    从底部开始。除了底部已经有砖块的情况外,该行顶部的任何砖块都会掉下来。所以,新的底行是:

    bottom_new = bottom_old OR top_old

    新的顶部是:

    top_new = bottom_old AND top_old

    也就是说,如果任一行都有砖,则新的底行会有砖,但如果两行都有砖,则新的顶行只会有砖。

    然后,您只需按自己的方式向上堆栈,新的顶行成为下一步的旧底行。

    【讨论】:

    • 我也在考虑这种方法,但是您需要多次重复此过程,因为如果您只执行此过程一次,砖不会掉落超过一排。 (例如,想象在顶行中只有一个砖块——它只会向下移动一行。)在最坏的情况下,您需要使用两个嵌套循环对 N 行重复该过程 N 次。但考虑到 ANDOR 操作的速度有多快,这不是一个坏主意,可能比我发布的解决方案更快。
    • 我认为这个问题是关于计算单个步骤的结果。我想不出比你或 shole 发布的更好的方法来计算最终结果
    【解决方案3】:

    目前我能想到的唯一解决方案是使用嵌套的for 循环:

    • vN 整数的输入向量
    • D 是每个整数的位数
    • c 跟踪砖块可能掉落的最底部的可用空间

    该算法检查数字n 中的ith 位是否使用(n &amp; (1&lt;&lt;i)) 设置,这适用于大多数类C 语言。

    C 中的算法:

    for (int j=0; j<D; ++j)
        int bit = 1<<j;
        int c = N-1;
        for (int i=N-1; i>=0; --i)
            if (v[i] & bit) {   // if bit j of number v[i] is set...
                v[i] ^= bit;    // set bit j in the number i to 0 using XOR
                v[c] ^= bit; // set bottom-most bit in the number i to 1 using XOR
                c -= 1;       //increment by bottom row 1 
            }
    

    如果N 很小并且预先知道它,您可以计算出每个数字的值的真值表,并仅使用按位运算而不使用循环得到正确的结果。

    【讨论】:

    • 抱歉,我不明白 r 是什么。你可以解释吗?我被告知它可以在5分钟内解决。所以我认为逻辑不会那么复杂。
    • r[j] 跟踪我们在jth 数字/列中相对于地面“建立”了1 的高度。当您说“在 5 分钟内解决”时,您是指程序运行时间,还是考虑解决方案所需的时间。这可能值得更多思考以获得更好的答案。
    • “5 分钟内解决”是指想出解决方案所需的时间。
    • 显然我没那么聪明。 :) 我会继续考虑的。
    • 编辑了我的答案以使其更简单。颠倒循环的顺序也使它稍微快一些。
    【解决方案4】:

    解决方案:

    所以我找到了一个我猜需要递归的解决方案。虽然我不知道停止递归的条件。

    向量 v = [3, 7, 8] 非常简单,无法解释为什么需要递归,所以我正在考虑一个新向量 v = [3, 9, 7, 8, 5]

    二进制形式:

    0 0 1 1 - a4  
    1 0 0 1 - a3  
    0 1 1 1 - a2  
    1 0 0 0 - a1  
    0 1 0 1 - a0 
    

    迭代 1:

    0 0 0 0 - b7  (b7 = a4 AND b5)  
    0 0 1 1 - b6  (b6 = a4 OR b5)  
    0 0 0 0 - b5  (b5 = a3 AND b3) ignore this  
    1 0 0 1 - b4  (b4 = a3 OR b3)
    0 0 0 0 - b3  (b3 = a2 AND b1) ignore this  
    0 1 1 1 - b2  (b2 = a2 OR b1)  
    0 0 0 0 - b1  (b1 = a0 AND a1) ignore this    
    1 1 0 1 - b0  (b0 = a0 OR a1)  
      
    Intermediate vector = [b7, b6, b4, b2, b0] = [0, 3, 9, 7, 13]
    

    迭代 2:

    0 0 0 0 - c7  (c7 = b4 AND c5)  
    0 0 0 1 - c6  (c6 = b4 OR c5)  
    0 0 0 1 - c5  (c5 = b3 AND c3) ignore this  
    0 0 1 1 - c4  (c4 = b3 OR c3)  
    0 0 0 1 - c3  (c3 = b2 AND c1) ignore this   
    1 1 0 1 - c2  (c2 = b2 OR c1)  
    0 1 0 1 - c1  (c1 = b0 AND b1) ignore this   
    1 1 1 1 - c0  (c0 = b0 OR b1)  
      
    Intermediate vector = [c7, c6, c4, c2, c0] = [0, 1, 3, 13, 15]  
    

    迭代 3:

    0 0 0 0 - d7 (d7 = c4 AND d5)  
    0 0 0 1 - d6 (d6 = c4 OR d5)  
    0 0 0 1 - d5 (d5 = c3 AND d3) ignore this  
    0 0 0 1 - d4 (d4 = c3 OR d3)  
    0 0 0 1 - d3 (d3 = c2 AND d1) ignore this      
    1 1 1 1 - d2 (d2 = c2 OR d1)  
    1 1 0 1 - d1 (d1 = c0 AND c1) ignore this 
    1 1 1 1 - d0 (d0 = c0 OR c1)  
      
    Resultant vector = [d7, d6, d4, d2, d0] = [0, 1, 1, 15, 15]  
    

    我通过向后遍历向量得到了这个解决方案。

    另一种解决方案:

    1. 用向量中所有元素的所有位构造一个多维数组(即如果 v = [3,7,8],则构造一个 3x4 数组并存储所有位。
    2. 计算每列中 1 的数量并存储计数。
    3. 从低位开始用 1 的计数填充每一列。

    这种方法很简单,但需要构造大型矩阵。

    【讨论】:

    • 我认为您的第一个解决方案非常好。检查停止条件实际上可能比简单地对N 行进行N 迭代更糟糕(这将保证所有砖块都已到位)。所有这些将是2*N*(N-1) 位运算,这可能比我的要快。我试图让砖块立即落到位,但我不得不做更多的操作来为此付出代价。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-11
    • 2016-05-28
    • 2016-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多