Olej писал(а): ↑29.01.2016 13:47№1 : Известно, что правильная рациональная дробь N / M ( N < M ) в десятичной записи может давать либо конечную запись ( 2 / 5 = 0.4 ), либо периодическую запись ( 1 / 3 = 0.(3) ). Рациональная дробь не может производить иррациональное значение (с непериодической десятичной записью, как, например, SQRT( 2 ) ). Создайте программу преобразования рациональной дроби в позиционную запись. Примите во внимание, что период может начинаться не с 1-й цифры после запятой: 1 / 12 = 0.08(3). Чтобы задача не казалась слишком лёгкой, сделайте её для произвольной системы счисления, основание которой (не только 10) вводится как отдельный параметр, например в 2-чной системе 2 / 3 = 0.(10) = 1 * ½ + 0 * ¼ + ...
Код: Выделить всё
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
inline char simb( unsigned val ) {
   return val < 10 ? '0' + val : 'A' + val - 10;
}
unsigned input( char *msg ) {
   int i;
   unsigned ret = 0;
   printf( "%s", msg );
   fflush( stdout );
   if( ( i = scanf( "%u", &ret ) ) <= 0 || ret <= 0 ) {
      printf( "ошибка ввода\n" );
      exit( 1 );
   }
   return ret;
}
void show( int n, unsigned ost, unsigned arr[] ) { // только для отладки:
   unsigned i;
   printf( "%3u -> {[%d]: ", ost, n );
   for( i = 0; i < n && arr[ i ] >= 0; i++ )
      printf( "%u ", arr[ i ] );
   printf( "}\n" );
};
int main( int argc, char **argv ) {
   int i, j, debug = ( argc > 1 &&
                       ( 0 == strcmp( argv[ 1 ], "-v" ) ||
                         0 == strcmp( argv[ 1 ], "debug" ) ) );
   while( 1 ) {
      unsigned metr, ch, zn;
      metr = input( "система счисления: " );
      ch = input( "числитель: " );
      zn = input( "знаменатель: " );
      if( ch >= zn ) {
         printf( "должнв быть правльная дробь!\n" );
         continue;
      }
      char *sval = (char*)calloc( zn + 1, sizeof( char ) );
      unsigned *list = (unsigned*)calloc( zn, sizeof( unsigned ) );
      for( i = 0; i < zn; i++ ) list[ i ] = -1;
      unsigned ost = ch;                    // остаток от деления
      for( i = 0; ; i++, ost *= metr ) {
         if( i > 0 ) sval[ i - 1 ] = simb( ost / zn );
         ost %= zn;
         if( debug ) show( i, ost, list );
         if( 0 == ost ) break;
         for( j = 0; j < i; j++ )
            if( ost == list[ j ] ) break;   // найден совпадающий остаток
         if( i == 0 || j == i )
            list[ i ] = ost;
         else {
            ost = i - j;
            break;
         }
      }
      sval[ i ] = '\0';
      free( list );
      char *sres = (char*)calloc( strlen( sval ) + 4, sizeof( char ) );
      strcpy( sres, "0." );
      i = strlen( sval ) - ost;
      strncpy( sres + strlen( sres ), sval, i );
      if( ost > 0 ) {
         strcat( sres, "(" );
         strcat( sres, sval + i );
         strcat( sres, ")" );
      }
      printf( "длина периода %u : %u / %u = %s\n", ost, ch, zn, sres );
      free( sval );
      free( sres );
   }
   return 0;
}Если с отладкой, чтобы видеть как это происходит:
Код: Выделить всё
$ ./period1 debug
система счисления: 10
числитель: 11
знаменатель: 13
 11 -> {[0]: }
  6 -> {[1]: 11 }
  8 -> {[2]: 11 6 }
  2 -> {[3]: 11 6 8 }
  7 -> {[4]: 11 6 8 2 }
  5 -> {[5]: 11 6 8 2 7 }
 11 -> {[6]: 11 6 8 2 7 5 }
длина периода 6 : 11 / 13 = 0.(846153)Если без (в конечном виде) + несколко результатовв качестве тестовых данных, если кому-то будет интересно улучшить и сравнить код:
Код: Выделить всё
$ ./period1
система счисления: 2
числитель: 17
знаменатель: 47
длина периода 23 : 17 / 47 = 0.(01011100100110001000001)
система счисления: 8
числитель: 53
знаменатель: 59
длина периода 58 : 53 / 59 = 0.(7137352234150105330745756511606404255436276724470320212661)
система счисления: 10
числитель: 283
знаменатель: 293
длина периода 146 : 283 / 293 = 0.(96587030716723549488054607508532423208191126279863481228668941979522184300341296928327645051194539249146757679180887372013651877133105802047781569)