2010-02-08 12 views
12

Como una asignación opcional, estoy pensando en escribir mi propia implementación de la clase BigInteger, donde voy a dar mis propios métodos para la suma, resta, multiplicación, etc.¿Qué estructura de datos debería usar para crear mi propia clase "BigInteger"?

Esto será para los números enteros arbitrariamente largas, incluso cientos de dígitos de largo.

Al hacer los cálculos en estos números, dígito por dígito no es difícil, ¿cuál cree que es la mejor estructura de datos para representar mi "BigInteger"?

Al principio estaba considerando usar una matriz, pero luego pensé que todavía podría desbordarme (quedarme sin ranuras de la matriz) después de una gran adición o multiplicación. ¿Sería este un buen caso para usar una lista vinculada, ya que puedo agregar dígitos con O (1) complejidad de tiempo?

¿Hay alguna otra estructura de datos que sería incluso más adecuada que una lista vinculada? ¿Debería el tipo de mi estructura de datos ser el tipo entero posible más pequeño que tengo disponible?

Además, ¿debo tener cuidado acerca de cómo almacenar mi variable "carry"? ¿Debería ser él mismo de mi tipo "BigInteger"?

+2

(a) No creo que deba usar una lista vinculada, estoy seguro de que algunas operaciones requerirán (o se beneficiarán de) acceso aleatorio.Además, las listas vinculadas son lentas con todas las asignaciones de memoria. (b) Si usa el entero más pequeño, usará la memoria más baja, pero si usa lo que coincida con el tamaño de una palabra (es decir, un 'int'), será rápido. Entonces depende de cuál es tu principal preocupación. Una posibilidad obvia es hacer que el entero escriba un parámetro de plantilla de su clase. (c) Compruebe la biblioteca de GNU MP, no se equivocará si copia algunas de sus decisiones de diseño. – Manuel

+1

Aquí está el código fuente de la clase BigInteger de Java: http://kickjava.com/src/java/math/BigInteger.java.htm – Manuel

Respuesta

0

Diría una serie de entradas.

+2

¿Podría al menos abordar los problemas que mencioné con respecto a esa implementación? – Fifa89

0

que diría un std :: vector de char (ya que tiene que contener sólo 0-9) (si va a trabajar en BCD)

Si no BCD a continuación, utilizar el vector de enteros (que aún no ha hacer claro)

Mucho menos espacio superior que enlazan lista

y cualquier asesoramiento dice el 'uso de vectores a menos que tenga una buena razón para no demasiado'

+0

umm ... Creo que las matemáticas binarias serían mejores que trabajar con los 10 números básicos aquí ... – Inverse

+0

umm - BCD es una implementación commonint común. No dijo lo que planeaba hacer – pm100

0

una matriz es de hecho un ajuste natural. Creo que es aceptable lanzar OverflowException, cuando te quedas sin memoria. El maestro verá atención a los detalles.

Una multiplicación duplica los números de un dígito, además aumenta a lo sumo 1. Es fácil crear una matriz lo suficientemente grande para almacenar el resultado de su operación.

Llevar a lo sumo un número largo de un dígito en la multiplicación (9 * 9 = 1, llevar 8). Un single int servirá.

+0

La multiplicación da como resultado aproximadamente dígitos de digits_a + digits_b + [0-2] en el producto ... que no es más que el doble del número en la entrada más grande, pero podría ser considerablemente menor que eso. – dmckee

1

Utilice siempre el tipo int más pequeño que hará el trabajo que necesita (bytes). Una lista vinculada debería funcionar bien, ya que no tendrá que preocuparse por el desbordamiento.

+0

Lo escribiría en la base 2 ** (machine_word-1) (base 65536 para máquinas de 32 bits). ¿Por qué perder tiempo procesando bytes individuales, cuando puede procesar palabras completas a la vez? –

+0

Desbordamiento no es realmente un problema, puede usar un vector o un deque que será mucho más eficiente que una lista vinculada para esta aplicación. – Manuel

+4

Me niego a creer que 2 ** 31 sea 65536 – Ponkadoodle

3

Revisa el libro C Interfaces and Implementations de David R. Hanson. Tiene 2 capítulos sobre el tema, que cubren la estructura del vector, el tamaño de la palabra y muchos otros temas que es probable que encuentre.

Está escrito para C, pero la mayoría es aplicable a C++ y/o Java. Y si usa C++, será un poco más simple porque puede usar algo como std::vector para administrar la asignación de matriz por usted.

+0

http://code.google.com/p/cii/source/browse/trunk/src/ap.c http://code.google.com/p/cii/source/browse/trunk/examples /calc.c – slf

1

Si utiliza árboles binarios (cuyas hojas son ints), obtiene todas las ventajas de la lista vinculada (número ilimitado de dígitos, etc.) con algoritmos de división y conquista más simples. En este caso, no tiene una base única, pero muchas dependen del nivel en el que está trabajando.

Si hace esto, debe usar un BigInteger para el transporte.Puede considerar una ventaja del enfoque de "lista enlazada de ints" que el acarreo siempre se puede representar como un int (y esto es cierto para cualquier base, no solo para la base 10, ya que la mayoría de las respuestas parecen suponer que usted debería usar. .. En cualquier base, el acarreo es siempre un solo dígito)

Mejor diré: sería una pérdida terrible usar la base 10 cuando puede usar 2^30 o 2^31.

1

El acceso a los elementos de las listas vinculadas es lento. Creo que las matrices son el camino a seguir, con una gran cantidad de comprobaciones consolidadas y el cambio de tamaño de la matriz en tiempo de ejecución, según sea necesario.


Aclaración: Recorrido de una lista enlazada y atravesando una matriz son ambos O (n) operaciones. Pero atravesar una lista vinculada requiere una referenciación de un puntero en cada paso. El hecho de que dos algoritmos tengan la misma complejidad no significa que ambos tomen el mismo tiempo para ejecutarse. La sobrecarga de asignar y desasignar n nodos en una lista vinculada también será mucho más pesado que la administración de memoria de una sola matriz de tamaño n, incluso si la matriz debe redimensionarse varias veces.

+1

¿Cómo son lentos? No estoy haciendo acceso aleatorio para ninguna de estas operaciones, solo secuencialmente. Esto haría que la lista enlazada sea tan rápida como el vector para el acceso secuencial y una mejor velocidad para las inserciones. – Fifa89

+0

@ Fifa89: una lista vinculada es más rápida para las inserciones en el medio, pero para lo que desea hacer agregará elementos al final, y para eso un vector o un deque son igual de rápidos. – Manuel

+0

@Manuel, sigo viendo que dices esto, pero ¿puedes dar una respuesta que lo muestre? Por lo que yo sé, una lista enlazada proporciona O (1) tiempo de inserción, y el vector proporciona una inserción O (1) amortizada. ¿Puedes dar más detalles sobre lo que hace que el vector sea más rápido? – Fifa89

0

std::vector<bool> o std::vector<unsigned int> es probablemente lo que desea. Tendrás que push_back() o resize() en ellos ya que necesitas más espacio para multiplicar, etc. Además, recuerda push_back los bits de signo correctos si estás usando dos cumplidos.

+2

No consideraría std :: vector una opción, para ser honesto – Manuel

0

Como regla general, utilice std::vector en lugar de std::list, a menos que necesite insertar elementos en el medio de la secuencia muy a menudo. Los vectores tienden a ser más rápidos, ya que se almacenan contiguamente y, por lo tanto, se benefician de una mejor ubicación espacial (un factor de rendimiento importante en las plataformas modernas).

Asegúrese de utilizar elementos naturales para la plataforma. Si quiere ser independiente de la plataforma, use long. Recuerde que a menos que tenga algunos intrínsecos especiales compiladores disponibles, necesitará un tipo al menos dos veces más grande para realizar la multiplicación.

No entiendo por qué querría llevar para ser un número entero grande. Carry es un solo bit para agregar y tamaño de elemento para la multiplicación.

Asegúrese de leer el Manual de programación de la computadora de Knuth, los algoritmos relativos a la aritmética de precisión arbitraria se describen allí en gran medida.

+0

¿Puedo preguntar por qué tiempo? ¿Hay alguna garantía de que esto se corresponda con el tamaño de la palabra de la arquitectura? – Manuel

+0

No hay, es solo una suposición educada (se asigna al tamaño de palabra natural bajo msvc y gcc en intel). – avakar

1

Guau, hay algunas ... respuestas interesantes aquí. Recomiendo leer un libro en lugar de tratar de ordenar todo este consejo contradictorio.

Dicho esto, C/C++ tampoco es adecuado para esta tarea. Big-integer es un tipo de matemática de precisión extendida. La mayoría de las CPU proporcionan instrucciones para manejar la matemática de precisión extendida a una velocidad comparable o igual (bits por instrucción) que las matemáticas normales. Cuando agrega 2^32 + 2^32, la respuesta es 0 ... pero también hay una salida de transporte especial de la ALU del procesador que un programa puede leer y usar.

C++ no puede acceder a esa bandera, y tampoco hay forma en C. Tienes que usar ensamblador.

Solo para satisfacer la curiosidad, puede usar la aritmética booleana estándar para recuperar bits de transporte, etc. Pero será mucho mejor descargar una biblioteca existente.

+1

Si quisiera usar una biblioteca existente, simplemente usaría la clase BigInteger que mencionó en su publicación. A veces las personas hacen cosas para aprender y no para ponerlas en un entorno de producción. Creo que esta es una buena tarea de CS y una que cada estudiante de CS debería hacer en algún momento. – mmcdole

+0

@simucal: también está aprendiendo el pequeño ensamblador que necesita para la multiplicación y la suma extendida, que también es educativo ... – Potatoswatter

Cuestiones relacionadas