我将整数值转换为三元数(这是一个字符串)。我怀疑应该有更好的方法来减少计算量。
我的算法直接实现了长除法,用于转换十进制和任何基数之间的数字。
char *get_ternary_str(int value) {
char buffer[3] = { "000" };
int dividend = value;
int i = 0;
do {
int remainder = dividend % 3;
dividend = dividend / 3;
buffer[i] = ((char)remainder + '0');
i++;
} while(dividend >= 1 && i < 3);
char temp = buffer[2];
buffer[2] = buffer[0];
buffer[0] = temp;
return buffer;
}
即使在这个例子中三元数是3位数,我的目标是使它长n位。关于如何对n位三进制数字进行此操作的建议也是受欢迎的。
答案 0 :(得分:3)
由于您要求提供建议,我不提供代码。但是,如果您需要进一步的帮助,可以在评论中注明。
答案 1 :(得分:3)
代码有问题。
返回指向本地变量的指针
返回指向本地(非静态)变量的指针是未定义行为(UB)。在功能完成后,不要依赖该存储器有效。
char *get_ternary_str(int value) {
char buffer[3] = ...
...
return buffer; // UB
}
因负数而失败
-1 % 3
- &gt; -1,因此remainder + '0'
不会生成数字字符。
int remainder = dividend % 3;
...
buffer[i] = ((char)remainder + '0');
空间不足
buffer[3]
对于"000"
的字符串而言太小,因为 null字符需要空格。 buffer[3]
足以容纳3位数字符的数组,但当然需要字符串。
// char buffer[3] = { "000" };
char buffer[3+1] = { "000" };
从未分配空字符
当然结果应该是字符串,它需要空字符 '\0'
。代码需要修改以确保。
我的目标是让它长到n位数 关于如何对n位三进制数字进行此操作的建议也是受欢迎的。
需要char
缓冲区来处理形成的字符串。一个简单的解决方案是传入一个足够大的缓冲区指针,以应对更糟糕的情况。我建议也传入缓冲区的大小。
以下是处理任何基础2-36的解决方案。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
// Maximum buffer size (worst case: INT_MIN and base 2)
#define ITOA_BASE_N (1 + sizeof(int)*CHAR_BIT + 1)
char *itoa_base(char *dest, size_t sz, int i, unsigned base) {
char buf[ITOA_BASE_N];
char *s = &buf[sizeof buf - 1]; // set to last character.
*s = '\0';
unsigned u = (unsigned) i; // Use unsigned to cope with INT_MIN ...
if (i < 0) {
u = -u; // ... as -INT_MIN is UB
}
do {
*(--s) = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[u % base];
u /= base;
} while (u);
if (i < 0) {
*(--s) = '-';
}
size_t size_used = &buf[sizeof buf] - s;
if (size_used > sz) {
return NULL; // or some other error handling.
}
return memcpy(dest, s, size_used);
}
测试使用复合文字进行临时存储,但如果需要,可以传入固定缓冲区。
#define TO_BASE3(x) itoa_base((char [ITOA_BASE_N]){0}, ITOA_BASE_N, (x), 3)
void test3(int x) {
char *s = TO_BASE3(x);
// do stuff with `s`, it is valid for until the end of this block
printf("base10:%11d base3:'%s'\n", x, s);
}
int main(void) {
test3(0);
test3(1);
test3(42);
test3(-81);
test3(INT_MAX);
test3(INT_MIN);
}
输出
base10: 0 base3:'0'
base10: 1 base3:'1'
base10: 42 base3:'1120'
base10: -81 base3:'-10000'
base10: 2147483647 base3:'12112122212110202101'
base10:-2147483648 base3:'-12112122212110202102'
答案 2 :(得分:1)
我在special_user
函数中分配动态内存并传递指向该函数的指针。这样我就可以管理内存。对于每个分配,您需要重新分配。我为字符串终止分配了超过字符串长度的1个元素。你的算法几乎不变。我更改了缓冲区的索引以避免重新排序缓冲区元素。
main
根据Y.Doktur的想法,这里的动态长度是相同的代码。我使用指向指针的指针来传递和接收指针。
#include<stdio.h>
#include<stdlib.h>
void get_ternary_str(char* buffer, int value, unsigned int n) {
int dividend = value;
int i = 0;
do {
int remainder = dividend % 3;
dividend = dividend / 3;
buffer[n - 1 - i] = ((char)remainder + '0');
i++;
} while(i < n);
buffer[i] = 0;
}
int main() {
int n = 5;
char* buffer = (char*) malloc(n * sizeof(char));
int value = 31;
get_ternary_str(buffer, value, n - 1);
printf("%s", buffer);
free(buffer);
return 0;
}
答案 3 :(得分:1)
如果您需要考虑,可以使用一个简单的小型查找表来交换一些内存空间用于计算时间。
考虑一下这个C ++(你的问题的早期版本也要求用这种语言提出想法)片段作为我提议的算法的一个例子。
#include <iostream>
#include <array>
#include <vector>
#include <string>
#include <iomanip>
#include <algorithm>
#include <limits>
std::string ternary_from(int value)
{
// The idea is to elaborate three figures (in base 3) at a time
constexpr int dim = 3 * 3 * 3;
// Note the values, are "reversed"
static const std::array<std::string, dim> inner_parts {
"000", "100", "200", "010", "110", "210", "020", "120", "220",
"001", "101", "201", "011", "111", "211", "021", "121", "221",
"002", "102", "202", "012", "112", "212", "022", "122", "222"
};
static const std::array<std::string, dim> parts {
"", "1", "2", "01", "11", "21", "02", "12", "22",
"001", "101", "201", "011", "111", "211", "021", "121", "221",
"002", "102", "202", "012", "112", "212", "022", "122", "222"
};
if ( value == 0 )
return std::string{"0"};
std::string result;
// Thanks @Chux for recalling that -INT_MIN is UB
unsigned tmp = value;
if ( value < 0 )
tmp = -tmp;
// note that 'dim' = 27, so you are performing a third of the calculations
while (tmp >= dim)
{
unsigned remainder = tmp % dim;
// concatenating a string at the end is easier...
result += inner_parts[remainder];
tmp = tmp / dim;
}
result += parts[tmp];
if (value < 0)
result += '-';
// now the string needs to be reversed. e.g. 3 -> 10(3), not 01
std::reverse(result.begin(), result.end());
return result;
}
int main()
{
std::vector<int> tests {
42, -8, 0, 81, 27, -28, std::numeric_limits<int>::max(), std::numeric_limits<int>::min()
};
std::cout << " decimal ternary\n";
for (int i : tests)
{
std::cout << std::setw(12) << i << std::setw(23) << ternary_from(i) << '\n';
}
}
输出
decimal ternary 42 1120 -8 -22 0 0 81 10000 27 1000 -28 -1001 2147483647 12112122212110202101 -2147483648 -12112122212110202102