5 Best Ways to Find the Largest Number Possible from a List of Numbers in Python

πŸ’‘ Problem Formulation: You are given a list of non-negative integers and your task is to arrange them such that they form the largest possible number. For instance, given [3, 34, 302, 50, 31] the largest formed number would be "5034330231".

Method 1: Custom Sort with String Comparison

To determine the correct order of numbers, we compare them as strings. Specifically, for any two numbers, say x and y, we compare the concatenated strings “xy” and “yx”, and arrange the numbers based on which concatenation yields a larger number. This method relies on the Python sorted function with a custom comparator.

Here’s an example:

def largest_num(nums):
    if not all(map(lambda x: type(x) == int and x >= 0, nums)):
        raise ValueError("All elements must be non-negative integers.")
        
    largest = ''.join(sorted(map(str, nums), reverse=True, key=lambda i: i*5))
    return '0' if largest[0] == '0' else largest

print(largest_num([3, 34, 302, 50, 31]))

Output: "5034330231"

This code snippet defines a function largest_num which first verifies that all elements in the list are non-negative integers, then sorts the numbers as strings, concatenating them in the order that produces the largest possible number. Zero-padding handling is included to ensure the output is not ‘0’ repeated.

Method 2: Using functools.cmp_to_key

The functools module provides a cmp_to_key function that converts a comparison function into a key function suitable for the sorted() method. The key function must be able to take two arguments and return a negative, zero, or positive number depending on the desired order. By defining a custom compare function, we can sort the numbers as per their concatenation results.

Here’s an example:

from functools import cmp_to_key

def compare(x, y):
    return (int(y+x) - int(x+y))

def largest_num(nums):
    nums = map(str, nums)  # Convert all numbers to strings
    sorted_nums = sorted(nums, key=cmp_to_key(compare), reverse=True)
    return ''.join(sorted_nums)

print(largest_num([3, 34, 302, 50, 31]))

Output: "5034330231"

The compare function implements the desired ordering by calculating the difference between the two possible concatenations. The largest_num function sorts the array of strings using this comparator, then joins and returns the result.

Method 3: Using a Max Heap

This method entails building a max heap from the list after transforming each number into a string. By comparing these strings lexicographically, we maintain a heap in which the top element represents the largest number when combined with others. The built-in library heapq can be used here.

Here’s an example:

import heapq

def largest_num(nums):
    nums = list(map(str, nums))  # Convert all numbers to strings
    heapq._heapify_max(nums)  # Arrange numbers to form a max heap
    largest = ''.join([heapq._heappop_max(nums) for _ in range(len(nums))])
    return largest

print(largest_num([3, 34, 302, 50, 31]))

Output: "5034330231"

The largest_num function creates a max-heap from the list of stringified numbers and then pops out elements from the heap to form the largest number. Keep in mind that the underscore functions of heapq are used for illustrative purposes; they are considered private and may change in future Python releases.

Method 4: Recursive Backtracking

Recursive backtracking is a brute force method which involves generating all possible permutations of the list’s elements to find the maximum number. This method guarantees the result but is not efficient for large lists due to its factorial time complexity.

Here’s an example:

from itertools import permutations

def largest_num(nums):
    max_num = '0'
    for perm in permutations(map(str, nums)):
        max_num = max(max_num, ''.join(perm))
    return max_num

print(largest_num([3, 34, 302, 50, 31]))

Output: "5034330231"

The snippet uses itertools.permutations which provides all possible arrangements of the list elements. The one which leads to the largest concatenated number is selected as max_num and returned.

Bonus Method 5: Concise One-Liner

The previous methods can be condensed into a one-liner using Python’s sorted() function combined with the join operation. This method provides a quick and easy solution but may sacrifice a bit of readability.

Here’s an example:

print(''.join(sorted(map(str, [3, 34, 302, 50, 31]), key=lambda x: x*5, reverse=True)))

Output: "5034330231"

This one-liner performs a similar custom sort as Method 1 but is presented in a compact form. Notice the lambda x: x*5 trick, which ensures that the comparisons are made by sufficiently long concatenated string repetitions so that the order is determined correctly.

Summary/Discussion

  • Method 1: Custom Sort with String Comparison. It’s efficient and easy to understand. However, it may not be intuitive for those unfamiliar with sorting algorithms.
  • Method 2: Using functools.cmp_to_key. Works well and leverages Python’s standard library, making the code clean. Can be tricky to handle edge cases like leading zeros.
  • Method 3: Using a Max Heap. Provides good performance. However, it requires understanding of heap data structures, and the use of Python’s private heap methods is not recommended.
  • Method 4: Recursive Backtracking. Though it’s not efficient for large lists, it can be useful for small sets and is straightforward in its approach.
  • Method 5: Concise One-Liner. It’s a quick, clever solution for simple scenarios. But it may be less readable and harder to debug.