C :: Aufgabe #149

1 Lösung Lösung öffentlich

Kaprekar-Konstanten für Hexadezimalzahlen

Fortgeschrittener - C von hollst - 27.02.2017 um 19:32 Uhr
Man finde für Zahlen in Hexadezimaldarstellung (Basis 16) alle Kaprekar-Konstanten (sofern vorhanden)
für 3, 4 ... 8stellige Zahlen entsprechend dem Algorithmus aus Aufgabe #165. Die Methode ist
so zu formulieren, dass sie leicht für alle Zahlenbasen von 2 bis 256 verwendbar ist.

Lösungen:

vote_ok
von devnull (8870 Punkte) - 08.03.2017 um 09:12 Uhr

Konsolenausgabe:

$ ./hkaprekar
max. 10-stellige Zahl eingeben: ABCh
ABCh
1FEh
DF2h
CF3h
BF4h
AF5h
9F6h
8F7h
7F8h
7F8h
Die Kaprekar-Konstante (9 Iterationen) ist:
7F8h

Quellcode ausblenden C-Code
/********************************************************
 * kaprekarb.c   Kaprekar-Konstante für allgemeine Basis
 ********************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define MAX_BASE_SIZE   256
#define MAX_DIGITS_SIZE  10
#define MAX_ITERATIONS  100
#define DIGIT_DELIMITER  '.'
#define BASE_DELIMITER   ':'

#define swap(a,b,t) {t=(a);a=(b);b=(t);}   /* swap values for sorting */

/* type definition based number */
typedef struct _bnumber {
	int base;
	int ndigits;
	int digits[MAX_DIGITS_SIZE];
} bnumber_t;

enum so_t {ASCENDING, DESCENDING};         /* sorting order */

/* usage */
void print_usage(void) {
	printf("%s\n",
	"Usage:\n"
	"       Basis <= 16: ZZZ...Z[bho]\n"
	"                    Z = [0-9a-fA-F]\n"
	"                    b : binär\n"
	"                    h : hexadezimal\n"
	"                    o : oktal\n\n"
	"       Basis  > 16: Z.Z.Z...Z:B\n"
	"                    Z = [0-9]+ mit Z<B\n"
	"                    B = {17..256}\n");
}

/* build based number from string */
int get_bnumber(char *bnstr, bnumber_t *bn) {
	char *pb;
	int last, n;
	size_t wp;

	if (bn == NULL || bnstr == NULL || strlen(bnstr)==0)
		return -1;

	bn->base = 0;
	bn->ndigits = 0;
	last = strlen(bnstr)-1;
	if ((pb = strrchr(bnstr, BASE_DELIMITER)) != NULL) {
		if ((bn->base = atoi(pb+1)) > MAX_BASE_SIZE)
			return -1;
		*pb = 0;
	}
	else {
		last = strlen(bnstr)-1;
		if (bnstr[last] == 'h') {
			bn->base = 16;
			bnstr[last--] = 0;
		}
		else if (bnstr[last] == 'o') {
			bn->base = 8;
			bnstr[last--] = 0;
		}
		else if (bnstr[last] == 'b') {
			bn->base = 2;
			bnstr[last--] = 0;
		}
		else if (bnstr[last] >= '0' && bnstr[last] <= '9')
			bn->base = 10;
		else
			return 1;
	}
	if (bn->base > 16) {
		n = 0;
		while ((pb = strrchr(bnstr, DIGIT_DELIMITER)) != NULL) {
			bn->digits[n++] = atoi(pb+1);
			wp = pb-bnstr;
			*pb = 0;
		}
		if (wp > 0)
			bn->digits[n++] = atoi(bnstr);
		bn->ndigits = n;
	}
	else {
		for (n=last,pb=bnstr; n>=0; n--,pb++) {
			if (*pb >= 'a' && *pb <= 'f')
				bn->digits[n] = *pb - 'a' + 10;
			else if (*pb >= 'A' && *pb <= 'F')
				bn->digits[n] = *pb - 'A' + 10;
			else if (*pb >= '0' && *pb <= '9')
				bn->digits[n] = *pb - '0';
			else
				return 1;
		}
		bn->ndigits = last+1;
	}
	/* check range of digits */
	for (n=0; n<bn->ndigits; n++)
		if (bn->digits[n] >= bn->base)
			return 1;
	return 0;
}

/* print based number */
void print_bnumber(bnumber_t *bn) {
	char chbase;
	int n;

	if (bn->base > 16) {
		for (n=bn->ndigits-1; n>=0; n--)
			printf("%d%c", bn->digits[n], (n>0)?DIGIT_DELIMITER:BASE_DELIMITER);
		printf("%d\n", bn->base);
	}
	else {
		switch (bn->base) {
		  case  2: chbase = 'b'; break;
		  case  8: chbase = 'o'; break;
		  case 16: chbase = 'h'; break;
		  case 10: chbase = 0;   break;
		  default: chbase = BASE_DELIMITER; break;
		}
		for (n=bn->ndigits-1; n>=0; n--)
			printf("%c", ((bn->digits[n]<10)?'0':('A'-10))+bn->digits[n]);
		if (chbase)
			printf("%c", chbase);
		if (chbase == BASE_DELIMITER)
			printf("%d", bn->base);
		printf("\n");
	}
}

/* copy based number */
void copy_bn(bnumber_t *obn, const bnumber_t *ibn) {
  int i;
  obn->base = ibn->base;
  obn->ndigits = ibn->ndigits;
  for (i=0; i < ibn->ndigits; i++)
	obn->digits[i] = ibn->digits[i];
}

/* compare based number */
int cmp_bn(const bnumber_t *ib1, const bnumber_t *ib2) {
  int i;
  for (i=ib1->ndigits-1; i>=0; i--)
	if (ib1->digits[i] != ib2->digits[i])
		return 1;
  return 0;
}

/* subtract based number */
void sub_bn(bnumber_t *obn, const bnumber_t *ib1, const bnumber_t *ib2) {
  int i, delta, carry=0;
  obn->base = ib1->base;
  obn->ndigits = ib1->ndigits;
  for (i=0; i < ib1->ndigits; i++) {
	delta = ib1->digits[i] - (ib2->digits[i] + carry);
	if (delta < 0) {
		delta += ib1->base;
		carry = 1;
	}
	else
		carry = 0;
	obn->digits[i] = delta;
  }
}

/* sorting with simple bubble sort */
void sort_bn(bnumber_t *obn, const bnumber_t *ibn, enum so_t so) {
  int i, j, t, sign;

  copy_bn(obn, ibn);
  sign = (so==DESCENDING)?-1:1;
  for (i=0; i < obn->ndigits-1; i++)
    for (j=0; j<obn->ndigits-i-1; j++)
      if (sign*obn->digits[j] > sign*obn->digits[j+1])
        swap(obn->digits[j], obn->digits[j+1], t);
}

/* main */
int main(int argc, char **argv) {
    char instr[256];
    bnumber_t d, d1, d2, dn;
    int count = 0;

	/* show help */
    if (argc > 1)
	  if ((strcmp(argv[1], "-h")==0) || (strcmp(argv[1], "help")==0)) {
		print_usage();
		exit(EXIT_SUCCESS);
	  }

    /* input number (w/o checking) */
    printf("max. %d-stellige Zahl eingeben: ", MAX_DIGITS_SIZE);
    scanf("%s", instr);
	if (get_bnumber(instr, &dn) != 0) {
		fprintf(stderr, "Illegal number format!\n");
		exit(EXIT_FAILURE);
	}
	print_bnumber(&dn);

    /* iteration */
    do {
		count++;
		copy_bn(&d, &dn);
		sort_bn(&d1, &dn, ASCENDING);
		sort_bn(&d2, &dn, DESCENDING);
		sub_bn(&dn, &d1, &d2);
		print_bnumber(&dn);
    } while (cmp_bn(&d, &dn) && count <= MAX_ITERATIONS);
    if (count > MAX_ITERATIONS)
		printf("Keine Konvergenz nach %d Iterationen.\n", MAX_ITERATIONS);
	else {
		printf("Die Kaprekar-Konstante (%d Iterationen) ist:\n", count);
		print_bnumber(&d);
	}
    return 0;
}