问题是。 有一个集合An,它由1到n的整数组成。
An = {1,2,3,...,n}
打印给定大小为K的An的所有子集。并且必须按照以下示例的顺序生成。
因此,例如,n = 5 k = 3
{1,2,3} {1,2,4} {1,2,5} {1,3,4} {1,3,5} {1,3,5} {1,3,5} {2,3, 4} ...... {3,4,5}
我不确定是否有其他方法不使用递归。我用递归做了这个,但问题是所有的测试用例都应该在1秒内完成。 当N和K像5,3和12,6时,它是可以的 当涉及50,48或100,95时,它需要太长时间。 所有问题都应该在1秒内完成。 我正在努力解决这个问题。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void subset(int n, int k, int arr[], int pos[], int index, int start){
int i, j;
if(index == k){
for(j=0; j<k; j++){
if(j==k-1)
printf("%d\n", pos[j]);
else{
printf("%d ", pos[j]);
}
}
return;
}
for(i=start; i<n; i++){
pos[index] = arr[i];
subset(n, k, arr, pos, index+1, i+1);
}
}
int main(){
int n, k, arr[100], index=0, start=0;
scanf("%d %d", &n, &k);
// 1<=n<=100 ; 1<=k<=n
if(n>100||n<1 && k>n||k<1)
return 0;
int i;
for(i=0; i<n; i++)
arr[i]=i+1;
int *pos = (int*)malloc(sizeof(int)*k);
time_t clockstart=0, clockend=0;
float gap;
clockstart = clock();
subset(n, k, arr, pos, index, start);
clockend = clock();
gap = (float)(clockend-clockstart)/(CLOCKS_PER_SEC);
printf("%f\n", gap);
return 0;
}
我认为我应该在C ++中使用类似尾递归或向量的东西。 但我无法弄清楚那些。
答案 0 :(得分:1)
提高速度算法的唯一方法&#34;不接触它是手动缓冲printf。
printf是一个在某个时间执行system call的函数。 每个系统调用都是昂贵的,这就是为什么每个执行某种系统调用的函数通常会执行“缓冲”#。
对于malloc来说,实际上,你分配了更多你认为的东西(malloc(1)不会通过分配1个八位字节而不是更多的东西来结束)当你释放内存时,它实际上是真的。没有真正发布(这样,如果你做另一个malloc,你不会进行系统调用但重用已释放的内存)。当然,它依赖于操作系统和实现依赖(都在引擎盖下)。 您可以使用&#34; strace&#34;。
在linux下看到一些系统调用同样适用于&#34; printf&#34; :因为它会进行系统调用,所以有一个缓冲区可以保留你想要打印的内容并且只是不时地进行打印。
那么当真正打印出printf的缓冲区时? 我们无法知道,它的实现是如何定义的(printf的手册页并没有说明有关printf缓冲的信息),但通常情况下,它可能是3次:
因为你做了一个&#34; \ n&#34;在每个子网,printf可能每次都要进行系统调用:它耗费时间。
通过使用缓冲区并在缓冲区中打印而不是stdout,可以加快代码速度:
#define BUF_LEN 2048
typedef struct mybuf {
char buffer[BUF_LEN];
size_t len;
} mybuf;
// For convenience, I use global varaible, but it's BAD
mybuf buf = {.buffer = "", .len = 0};
void MyBuf_PrintOnStdout(void)
{
write(1, buf.buffer, buf.len);
buf.len = 0;
}
void MyBuf_Flush(void)
{
MyBuf_PrintOnStdout();
fflush(stdout);
}
void MyBuf_PrintInteger(int integer)
{
int printedLen;
// An 64bit integer take 20digit + 1 char for potential "-"
// So 21 is the max len for an integer.
// Of course, if your int is bigger than 64bit, this if is false.
if (buf.len + 21 >= BUF_LEN) {
MyBuf_PrintOnStdout();
}
printedLen = sprintf(buf.buffer + buf.len, "%d", integer);
// Error check (printedLen < 0 == error)
buf.len += printedLen;
}
void MyBuf_PrintCharacter(char character)
{
if (buf.len + 1 >= BUF_LEN) {
MyBuf_PrintOnStdout();
}
buf.buffer[buf.len] = character;
++buf.len;
}
void subset(int n, int k, int arr[], int pos[], int index, int start)
{
if (index == k) {
for (int j = 0; j < k; ++j) {
MyBuf_PrintInteger(pos[j]);
MyBuf_PrintCharacter(j == k-1 ? '\n' : ' ');
}
return;
}
for(int i = start; i<n; i++){
pos[index] = arr[i];
subset(n, k, arr, pos, index+1, i+1);
}
}
别忘了打电话给#34; MyBuf_Flush&#34;最后,因为没有它你可能会遗漏一些印刷品。
编辑:使用complet代码,我做了一些测试。 虽然它是真的有改进(N = 30,k = 20,你的代码需要大约88s,而写作花费大约78s),但真的太差了,不能让它在不到1s的时间内工作。 没有超级计算器可以解决您的问题吗?
另一个编辑:好的,我已经混淆了&#34;应该&#34;并且&#34;必须&#34;,抱歉。英语不是我的母语。 (我认为你必须使用递归)。
由于你可以不使用递归,这里有一些有趣的东西: 我已经实现了递归而不是n = 30,k = 20的递归。 对于每个实现,我都启用并禁用了打印。
结果很清楚:
-
总而言之,打印部分确实需要时间,而不是找到解决方案本身。
这里没有递归实现:
#define BUF_LEN 4096
typedef struct mybuf {
char buffer[BUF_LEN];
size_t len;
} mybuf;
// For convenience, I use global varaible, but it's BAD
mybuf buf = {.buffer = "", .len = 0};
void MyBuf_PrintOnStdout(void)
{
/*buf.buffer[buf.len] = '\0';
printf("%s", buf.buffer);*/
write(1, buf.buffer, buf.len);
buf.len = 0;
}
void MyBuf_Flush(void)
{
MyBuf_PrintOnStdout();
fflush(stdout);
}
void MyBuf_PrintInteger(int integer)
{
int printedLen;
if (buf.len + 21 >= BUF_LEN) {
MyBuf_PrintOnStdout();
}
printedLen = sprintf(buf.buffer + buf.len, "%d", integer);
// Error check (printedLen < 0 == error)
buf.len += printedLen;
}
void MyBuf_PrintCharacter(char character)
{
if (buf.len + 1 >= BUF_LEN) {
MyBuf_PrintOnStdout();
}
buf.buffer[buf.len] = character;
++buf.len;
}
void subset_no_recursion(int n, int k)
{
int pos[k];
for (int i = 0; i < k; ++i) {
pos[i] = k - i;
}
for (;;) {
// Last digit incrementation
while (pos[0] <= n) {
/* print
for (int i = k - 1; i >= 0; --i) {
MyBuf_PrintInteger(pos[i]);
MyBuf_PrintCharacter(i == 0 ? '\n' : ' ');
}*/
++pos[0];
}
// We find where we can increment the digit without overflow N
int pivot = 1;
while (pos[pivot] == n - pivot) {
++pivot;
}
if (pivot == k) {
return;
}
++pos[pivot];
while (pivot) {
pos[pivot - 1] = pos[pivot] + 1;
--pivot;
}
}
}
void subset_recursion(int n, int k, int pos[], int index, int start)
{
if (index == k) {
for (int i = 0; i < k; ++i) {
MyBuf_PrintInteger(pos[i]);
MyBuf_PrintCharacter(i == k-1 ? '\n' : ' ');
}
return;
}
for (int i = start; i < n; i++) {
pos[index] = i + 1;
subset_recursion(n, k, pos, index + 1, i + 1);
}
}
#define N 30
#define K 20
int main(void)
{
int pos[K];
time_t clockstart;
time_t clockend;
clockstart = clock();
subset3(N, K);
clockend = clock();
printf("%f\n", ((float)(clockend - clockstart)) / CLOCKS_PER_SEC);
return 0;
}