我有以下问题,实际上是我最近采用的编码测试:
存在函数f(n) = a*n + b*n*(floor(log(n)/log(2))) + c*n*n*n
。
在特定值,让f(n) = k
;
给定k, a, b, c
,找到n
。
对于给定的k
值,如果不存在n
值,则返回0.
1 <= n < 2^63-1
0 < a, b < 100
0 <= c < 100
0 < k < 2^63-1
这里的逻辑是,由于f(n)
对于给定的a,b和c纯粹增加,我可以通过二分搜索找到n
。
我写的代码如下:
#include<iostream>
#include<stdlib.h>
#include<math.h>
using namespace std;
unsigned long long logToBase2Floor(unsigned long long n){
return (unsigned long long)(double(log(n))/double(log(2)));
}
#define f(n, a, b, c) (a*n + b*n*(logToBase2Floor(n)) + c*n*n*n)
unsigned long long findNByBinarySearch(unsigned long long k, unsigned long long a, unsigned long long b, unsigned long long c){
unsigned long long low = 1;
unsigned long long high = (unsigned long long)(pow(2, 63)) - 1;
unsigned long long n;
while(low<=high){
n = (low+high)/2;
cout<<"\n\n k= "<<k;
cout<<"\n f(n,a,b,c)= "<<f(n,a,b,c)<<" low = "<<low<<" mid="<<n<<" high = "<<high;
if(f(n,a,b,c) == k)
return n;
else if(f(n,a,b,c) < k)
low = n+1;
else high = n-1;
}
return 0;
}
然后我尝试了一些测试用例:
int main(){
unsigned long long n, a, b, c;
n = (unsigned long long)pow(2,63)-1;
a = 99;
b = 99;
c = 99;
cout<<"\nn="<<n<<" a="<<a<<" b="<<b<<" c="<<c<<" k = "<<f(n, a, b, c);
cout<<"\nANSWER: "<<findNByBinarySearch(f(n, a, b, c), a, b, c)<<endl;
n = 1000;
cout<<"\nn="<<n<<" a="<<a<<" b="<<b<<" c="<<c<<" k = "<<f(n, a, b, c);
cout<<"\nANSWER: "<<findNByBinarySearch(f(n, a, b, c), a, b, c)<<endl;
return 0;
}
然后发生了一些奇怪的事情。
该代码适用于测试用例n = (unsigned long long)pow(2,63)-1;
,正确返回n的值。但它对n=1000
无效。我打印输出并看到以下内容:
n=1000 a=99 b=99 c=99 k = 99000990000
k= 99000990000
f(n,a,b,c)= 4611686018427387904 low = 1 mid=4611686018427387904 high = 9223372036854775807
...
...
k= 99000990000
f(n,a,b,c)= 172738215936 low = 1 mid=67108864 high = 134217727
k= 99000990000
f(n,a,b,c)= 86369107968 low = 1 mid=33554432 high = 67108863
k= 99000990000
f(n,a,b,c)= 129553661952 low = 33554433 mid=50331648 high = 67108863**
...
...
k= 99000990000
f(n,a,b,c)= 423215328047139441 low = 37748737 mid=37748737 high = 37748737
ANSWER: 0
有些东西在数学上看起来不对。 f(1000)
的价值大于f(33554432)
的价值怎么样?
所以我在Python中尝试了相同的代码,并获得了以下值:
>>> f(1000, 99, 99, 99)
99000990000L
>>> f(33554432, 99, 99, 99)
3740114254432845378355200L
所以,价值肯定更大。
答案 0 :(得分:2)
问题在于:
unsigned long long low = 1;
// Side note: This is simply (2ULL << 62) - 1
unsigned long long high = (unsigned long long)(pow(2, 63)) - 1;
unsigned long long n;
while (/* irrelevant */) {
n = (low + high) / 2;
// Some stuff that do not modify n...
f(n, a, b, c) // <-- Here!
}
在第一次迭代中,您有low = 1
和high = 2^63 - 1
,这意味着n = 2^63 / 2 = 2^62
。现在,让我们看一下f
:
#define f(n, a, b, c) (/* I do not care about this... */ + c*n*n*n)
n^3
中有f
,因此对于n = 2^62
,n^3 = 2^186
,这可能对您的unsigned long long
来说太大了(可能是ull_t
64位长)。
这里的主要问题是在进行二进制搜索时溢出,所以你应该只是分别处理溢出的情况。
序言:我正在使用log
因为我很懒,你应该避免使用C ++中的宏,更喜欢使用函数并让编译器内联它。另外,我更喜欢使用unsigned long long
函数来计算log2
的log2的循环(请参阅本答案的底部以了解is_overflow
和using ull_t = unsigned long long;
constexpr auto f (ull_t n, ull_t a, ull_t b, ull_t c) {
if (n == 0ULL) { // Avoid log2(0)
return 0ULL;
}
if (is_overflow(n, a, b, c)) {
return 0ULL;
}
return a * n + b * n * log2(n) + c * n * n * n;
}
的实现)。
constexpr auto find_n (ull_t k, ull_t a, ull_t b, ull_t c) {
constexpr ull_t max = std::numeric_limits<ull_t>::max();
auto lb = 1ULL, ub = (1ULL << 63) - 1;
while (lb <= ub) {
if (ub > max - lb) {
// This should never happens since ub < 2^63 and lb <= ub so lb + ub < 2^64
return 0ULL;
}
// Compute middle point (no overflow guarantee).
auto tn = (lb + ub) / 2;
// If there is an overflow, then change the upper bound.
if (is_overflow(tn, a, b, c)) {
ub = tn - 1;
}
// Otherwize, do a standard binary search...
else {
auto val = f(tn, a, b, c);
if (val < k) {
lb = tn + 1;
}
else if (val > k) {
ub = tn - 1;
}
else {
return tn;
}
}
}
return 0ULL;
}
这是稍加修改的二进制搜索版本:
is_overflow(tn, a, b, c)
正如您所看到的,这里只有一个相关的测试,即lb + ub
(ub < 2^63
和lb <= ub < 2^63
之后关于ub + lb < 2^64
的第一个测试无关紧要所以unsigned long long
在我们的案例中对于#include <limits>
#include <type_traits>
using ull_t = unsigned long long;
template <typename T,
typename = std::enable_if_t<std::is_integral<T>::value>>
constexpr auto log2 (T n) {
T log = 0;
while (n >>= 1) ++log;
return log;
}
constexpr bool is_overflow (ull_t n, ull_t a, ull_t b, ull_t c) {
ull_t max = std::numeric_limits<ull_t>::max();
if (n > max / a) {
return true;
}
if (n > max / b) {
return true;
}
if (b * n > max / log2(n)) {
return true;
}
if (c != 0) {
if (n > max / c) return true;
if (c * n > max / n) return true;
if (c * n * n > max / n) return true;
}
if (a * n > max - c * n * n * n) {
return true;
}
if (a * n + c * n * n * n > max - b * n * log2(n)) {
return true;
}
return false;
}
constexpr auto f (ull_t n, ull_t a, ull_t b, ull_t c) {
if (n == 0ULL) {
return 0ULL;
}
if (is_overflow(n, a, b, c)) {
return 0ULL;
}
return a * n + b * n * log2(n) + c * n * n * n;
}
constexpr auto find_n (ull_t k, ull_t a, ull_t b, ull_t c) {
constexpr ull_t max = std::numeric_limits<ull_t>::max();
auto lb = 1ULL, ub = (1ULL << 63) - 1;
while (lb <= ub) {
if (ub > max - lb) {
return 0ULL; // Problem here
}
auto tn = (lb + ub) / 2;
if (is_overflow(tn, a, b, c)) {
ub = tn - 1;
}
else {
auto val = f(tn, a, b, c);
if (val < k) {
lb = tn + 1;
}
else if (val > k) {
ub = tn - 1;
}
else {
return tn;
}
}
}
return 0ULL;
}
是可以的。)
constexpr
下面是一小段代码,您可以使用它来检查上面的代码是否在编译时(因为所有内容都是template <unsigned long long n, unsigned long long a,
unsigned long long b, unsigned long long c>
struct check: public std::true_type {
enum {
k = f(n, a, b, c)
};
static_assert(k != 0, "Value out of bound for (n, a, b, c).");
static_assert(n == find_n(k, a, b, c), "");
};
template <unsigned long long a,
unsigned long long b,
unsigned long long c>
struct check<0, a, b, c>: public std::true_type {
static_assert(a != a, "Ambiguous values for n when k = 0.");
};
template <unsigned long long n>
struct check<n, 0, 0, 0>: public std::true_type {
static_assert(n != n, "Ambiguous values for n when a = b = c = 0.");
};
#define test(n, a, b, c) static_assert(check<n, a, b, c>::value, "");
test(1000, 99, 99, 0);
test(1000, 99, 99, 99);
test(453333, 99, 99, 99);
test(495862, 99, 99, 9);
test(10000000, 1, 1, 0);
):
k
注意: 2^63
的最大值约为(a, b, c)
,因此对于给定的三元组n
,f(n, a, b, c) < 2 ^ 63
的最大值为f(n + 1, a, b, c) >= 2 ^ 63
和a = b = c = 99
之类的内容。对于n = 453333
,此最大值为{{1}}(根据经验找到),这就是我在上面进行测试的原因。