英文:
Encoding a monomial [lx,ly,lz] to contiguous index and decoding back
问题
我想要帮助或指导您编写一个名为 encode
的过程,以计算如下代码片段中的 index
:
#include <iostream>
int encode(int lx, int ly, int lz) {
return (ly + lz) * (ly + lz + 3) / 2 - ly;
}
int main() {
int L;
// 用您期望的 L 值替换
for (L = 0; L <= 5; ++L) {
int index = 0;
for (int i = 0; i <= L; i++) {
int lx = L - i;
for (int j = 0; j <= i; j++, index++) {
int ly = i - j;
int lz = j;
std::cout << "[" << lx << "," << ly << "," << lz << "] ->" << index << " vs " << encode(lx, ly, lz) << std::endl;
}
}
std::cout << "-----------" << std::endl;
}
return 0;
}
上面的 encode
似乎可以工作。我想实现一个名为 decode
的过程,该过程根据给定的 index
计算 lx
、ly
和 lz
。预计经常调用 encode
和 decode
过程,因此它们的性能至关重要。因此,非常感谢任何提高它们速度的建议。
根据您的要求,我将描述 encode
的工作原理。
给定的过程 encode(int lx, int ly, int lz)
接受三个非负整数 lx
、ly
和 lz
作为输入。这些值表示一个单项式 (lx, ly, lz)
。
该过程基于单项式 (lx, ly, lz)
计算一个索引值。该索引表示将与单项式相关联的信息存储在连续数组中的索引。遵循单项式的规则如下:
- 组件
lx
、ly
和lz
的总和必须等于L
。 L
的值可以取从 0 到N
(包括)的整数值。lx
、ly
和lz
必须是非负整数。L
也必须是非负整数。
英文:
I want help or guidance to write one procedure called encode
to compute index
as in the following code snippet:
#include <iostream>
int encode(int lx, int ly, int lz) {
return (ly+lz)*(ly+lz+3)/2 - ly;
}
int main() {
int L;
// Replace with your desired value of L
for (L = 0; L <= 5; ++L) {
int index = 0;
for (int i = 0; i <= L; i++) {
int lx = L - i;
for (int j = 0; j <= i; j++, index++) {
int ly = i - j;
int lz = j;
std::cout << "[" << lx << "," << ly << "," << lz << "] ->" << index <<" vs " << encode(lx, ly, lz)<< std::endl;
}
}
std::cout << "-----------" << std::endl;
}
return 0;
}
'encode` above seems to work. I would like to implement a procedure named decode that calculates lx, ly, and lz based on the given index. Both encode and decode procedures are expected to be called frequently, making their performance crucial. Therefore, any suggestions to enhance their speed would be greatly appreciated.
As requested, I will describe how endode
works.
The given procedure, encode(int lx, int ly, int lz)
, takes three non-negative integers lx
, ly
, and lz
as input. These values represent a monomial (lx, ly, lz)
.
The procedure calculates an index value based on the monomial (lx, ly, lz)
. The index represents the index to store the information associated with the monomial in a contiguous array. The rules that follows the monomials are as follows:
- The sum of the components
lx
,ly
, andlz
must be equal toL
. - The value of
L
can take integer values from 0 toN
, inclusive. lx
,ly
, andlz
must be non-negative integers.L
must also be a non-negative integer.
答案1
得分: 1
以下是翻译好的部分:
初始解决方案
正如注释中所提到的,encode
函数可以被替换为 return (ly+lz)*(ly+lz+3)/2 - ly;
。这是因为编码本质上遵循了三角数的规律:当我们按照所需的索引顺序列出编码时,列表以具有最大第一个 lx 值(L)的一项开始,然后继续以具有较低 lx 值的下一个两项(L−1)开始,然后是具有较低值的三项,依此类推。因此,在当前第一个索引的开始之前的项目数量是第 n 个三角数,即 n(n+1)/2,其中 n 是 L−lx。这为我们提供了具有给定 lx 值的第一个三元组的编码。从那里,我们想要考虑 ly 值。它们也是按降序列出的,因此第一个值是 L−lx。随着它们的减少,我们希望在编码中添加更多内容,因此我们添加 L−lx−ly。
这给我们带来了 (L−lx)(L−lx+1)/2+L−lx−ly = (L−lx)(L−lx)/2-ly。当然,L−lx 实际上是 ly+lz,所以这是 (ly+lz)(ly+lz+3)−ly,因此 return (ly+lz)*(ly+lz+3)/2 - ly;
。
要反转这个过程,我们需要认识到,在具有给定 lx 值的第一个列表项上,L−lx−ly 的贡献为零,并且我们有三角数等于编码 e,因此 (L−lx)(L−lx+1)/2 = e。解这个二次方程以获得 lx,得到 lx = L + 1.5 - sqrt(2.25 + 2*e)。 (该二次方程有两个解,但我们感兴趣的是这一个,因为另一个会使 lx 大于 L。)
因此,给定一个编码 e,我们可以设置 lx = L + 1.5 - std::sqrt(2.25 + 2*e);
。当 e 是给定 lx 的第一个列表项的编码时,这是精确的。当 e 是具有不同 ly 和 lz 的相同 lx 的后续编码时,该公式会给出一个较高的值,但直到 lx 更改,它才会达到下一个整数。因此,在赋值中截断为整数会产生所有具有给定 lx 值的编码的正确 lx。
我将留下为读者找到 ly 的代数解,并将其表示为 ly = (L-lx)*(L-lx+3)/2 - e;
。当然,lz 只是 lz = L-lx-ly;
。
因此,decode
程序如下:
#include <cmath>
void decode(int L, int e, int &lx, int &ly, int &lz)
{
lx = L + 1.5 - std::sqrt(2.25 + 2*e);
ly = (L-lx)*(L-lx+3)/2 - e;
lz = L-lx-ly;
}
注意
上述要求 sqrt
在结果恰好为整数时给出正确的结果。在某些 C++ 实现中,sqrt
可能会给出计算不良的结果,因此可能需要进行调整,要么通过测试 lx
并在必要时进行增加,要么通过添加调整来解决,例如 L + 1.5 - std::sqrt(2.25 + 2*e) + 1e-10;
。可以基于对 L
的边界进行计算,来获得这种调整的合理值。
更新:一个不错的做法是在计算 lx
时从 e 中减去 1/2。当 2e 恰好是一个完全平方数时,这会将其移动到远离那个值的位置(在所需方向上,使其变小,从而将其从 L+1/2 减少到正确的整数),从而避免了 sqrt
中小误差的不良影响,但它不会将 e 的其他值推到下一个值,其中计算的 lx
值会发生变化。而且,由于 e 出现为 2e,我们可以通过从 2e 中减去 1,即使用 1/2+2e:lx = L + 1.5 - std::sqrt(1.25 + 2*e);
。
英文:
Initial Solution
As noted in the comments, the encode
function can be replaced by return (ly+lz)*(ly+lz+3)/2 - ly;
. This arises out of the fact that the encoding essentially follows triangular numbers: When we list the encodings in the desired index order, the list starts with one item with the maximum first lx value (L), and continues with two items with the next lower lx value (L−1), then three items with the next lower value and so on. So the number of items preceding the start of the current first index is the n<sup>th</sup> triangular number, n(n+1)/2, where n is L−lx. That gives us the encoding for the first triple with the given lx value. From there, we want to account for the ly values. These are also listed in descending order, so the first one is L−lx. As they descend, we want to add more to the encoding, so we add L−lx−ly.
That gives us (L−lx)(L−lx+1)/2+L−lx−ly = (L−lx)(L−lx)/2-ly. L−lx is of course ly+lz, so this is (ly+lz)(ly+lz+3)−ly, and hence return (ly+lz)*(ly+lz+3)/2 - ly;
.
To reverse this, we recognize that, at the first list item with a given value for lx, the L−lx−ly contribution is zero, and we have the triangular number equaling the encoding e, so (L−lx)(L−lx+1)/2 = e. Solving this quadratic equation for lx gives lx = L + 1½ - sqrt(2¼ + 2e). (The quadratic has two solutions, but this is the one of interest to us, as the other would make lx greater than L.)
Thus, given an encoding e, we can set lx = L + 1.5 - std::sqrt(2.25 + 2*e);
. This is exact when e is the encoding of the first list item for the given lx. When e is a later encoding for the same lx with different ly and lz, the formula gives a higher value, but it does not reach the next integer until lx changes. So the truncation to integer in the assignment yields the correct lx for all encodings with a given lx value.
I will leave the algebra for finding ly to the reader and give it as ly = (L-lx)*(L-lx+3)/2 - e;
. And of course lz is merely lz = L-lx-ly;
.
Thus a decode
routine is:
#include <cmath>
void decode(int L, int e, int &lx, int &ly, int &lz)
{
lx = L + 1.5 - std::sqrt(2.25 + 2*e);
ly = (L-lx)*(L-lx+3)/2 - e;
lz = L-lx-ly;
}
Caution
The above requires that sqrt
give a correct result when the result is exactly an integer. In C++ implementations where sqrt
may deliver a poorly computed result, it may be necessary to make an adjustment, either by testing lx
and incrementing if necessary or adding an adjustment, as with L + 1.5 - std::sqrt(2.25 + 2*e) + 1e-10;
. A reasonable value for such an adjustment could be computed based on a bound on L
.
Update: A nice way to do this is to subtract ½ from e in the calculation of lx
. When 2¼+2e would be a perfect square, this moves it away from that (in the desired direction, making it smaller so subtracting it from L+1½ produces a large value, which will be truncated to the proper integer), which avoids bad consequences of small errors in sqrt
, but it does not push other values of e across the next boundary where the calculated value of lx
would change. And, since e appears as 2e, we can do this by subtracting 1 from 2¼+2e, thus using 1¼+2e: lx = L + 1.5 - std::sqrt(1.25 + 2*e);
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论