AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79573357
Accepted
Ξένη Γήινος
Ξένη Γήινος
Asked: 2025-04-14 22:18:46 +0800 CST2025-04-14 22:18:46 +0800 CST 2025-04-14 22:18:46 +0800 CST

Qual é uma maneira mais rápida de encontrar todas as partições exclusivas de um inteiro e seus pesos?

  • 772

Tenho visto muitas postagens sobre esse assunto, mas nenhuma é exatamente o que estou procurando.

Quero encontrar todas as maneiras pelas quais um número inteiro positivo N maior que 1 pode ser expresso como a soma de no máximo N números inteiros de 1 a N, como de costume.

Por exemplo, na notação padrão, estas são todas as partições de 6:

[(1, 1, 1, 1, 1, 1),
 (1, 1, 1, 1, 2),
 (1, 1, 1, 2, 1),
 (1, 1, 1, 3),
 (1, 1, 2, 1, 1),
 (1, 1, 2, 2),
 (1, 1, 3, 1),
 (1, 1, 4),
 (1, 2, 1, 1, 1),
 (1, 2, 1, 2),
 (1, 2, 2, 1),
 (1, 2, 3),
 (1, 3, 1, 1),
 (1, 3, 2),
 (1, 4, 1),
 (1, 5),
 (2, 1, 1, 1, 1),
 (2, 1, 1, 2),
 (2, 1, 2, 1),
 (2, 1, 3),
 (2, 2, 1, 1),
 (2, 2, 2),
 (2, 3, 1),
 (2, 4),
 (3, 1, 1, 1),
 (3, 1, 2),
 (3, 2, 1),
 (3, 3),
 (4, 1, 1),
 (4, 2),
 (5, 1),
 (6,)]

Agora, a notação é de entropia muito baixa: primeiro, cada ocorrência do número aumenta o tamanho de uma partição específica. Isso é ineficiente e é difícil contar as ocorrências dos números quando eles se repetem muitas vezes. Quero substituir todas as ocorrências de um número por uma tupla de dois elementos, na qual o primeiro elemento é o número e o segundo é a contagem. Por exemplo, (1, 1, 1, 1, 1, 1)é equivalente a (1, 6). Ambos contêm a mesma informação, mas um é claramente muito mais conciso.

E segundo, há muitas duplicatas na saída; por exemplo, há cinco partições que contêm quatro 1s e um 2, que são contadas como cinco elementos separados. Isso também é ineficiente, já que a adição é comutativa; mudar a ordem dos números não altera o resultado, então todos são equivalentes, são todos o mesmo elemento.

Entretanto, se substituirmos todos os cinco por apenas um elemento, perdemos informações.

Em vez disso, desejo substituí-lo pelo seguinte formato:

Counter({((1, 2), (2, 2)): 6,
         ((1, 1), (2, 1), (3, 1)): 6,
         ((1, 4), (2, 1)): 5,
         ((1, 3), (3, 1)): 4,
         ((1, 2), (4, 1)): 3,
         ((1, 1), (5, 1)): 2,
         ((2, 1), (4, 1)): 2,
         ((1, 6),): 1,
         ((2, 3),): 1,
         ((3, 2),): 1,
         ((6, 1),): 1})

Então, quero que o resultado seja um Counterem que as chaves sejam as partições exclusivas e os valores sejam quantas maneiras os números podem ser organizados.

E sim, eu escrevi uma função para isso, usando força bruta e memorização. Ela se mostrou bem eficiente.

Primeiro, esta é a implementação que gera o resultado no formato padrão. Posto aqui para comparação:

def partitions(number: int) -> list[tuple[int, ...]]:
    result = []
    stack = [(number, ())]

    while stack:
        remaining, path = stack.pop()
        if not remaining:
            result.append(path)
        else:
            stack.extend((remaining - i, path + (i,)) for i in range(remaining, 0, -1))

    return result

São necessários 582 milissegundos para encontrar todas as partições de 20 no CPython e 200 milissegundos no PyPy3:

CPython

In [22]: %timeit partitions(20)
582 ms ± 4.22 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

PyPy3

In [36]: %timeit partitions(20)
199 ms ± 3.17 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Agora, a abordagem de força bruta com memorização que gera o formato pretendido:

PARTITION_COUNTERS = {}


def partition_counter(number: int) -> Counter:
    if result := PARTITION_COUNTERS.get(number):
        return result
    
    result = Counter()
    for i in range(1, number):
        for run, count in partition_counter(number - i).items():
            new_run = []
            added = False
            for a, b in run:
                if a == i:
                    new_run.append((a, b + 1))
                    added = True
                else:
                    new_run.append((a, b))
            
            if not added:
                new_run.append((i, 1))
            
            result[tuple(sorted(new_run))] += count
    
    result[((number, 1),)] = 1
    PARTITION_COUNTERS[number] = result
    return result

CPython

In [23]: %timeit PARTITION_COUNTERS.clear(); partition_counter(20)
10.4 ms ± 72.1 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

PyPy3

In [37]: %timeit PARTITION_COUNTERS.clear(); partition_counter(20)
9.75 ms ± 58.3 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Leva apenas 10 milissegundos para encontrar todas as partições de 20, muito, muito mais rápido que a primeira função, e o PyPy3 não o torna mais rápido.

Mas como podemos fazer melhor? Afinal, estou apenas usando força bruta. Sei que existem muitos algoritmos inteligentes para partição de números inteiros, mas nenhum deles gera saídas no formato pretendido.

python
  • 2 2 respostas
  • 170 Views

2 respostas

  • Voted
  1. Best Answer
    trincot
    2025-04-15T04:15:30+08:002025-04-15T04:15:30+08:00

    Será mais rápido se você gerar apenas os multiconjuntos classificados e, então, calcular para cada um quantas permutações distintas ele tem.

    Para isso, você pode usar a função Combinação , ou seja, 𝑛-escolha-𝑘, representado como 𝑛 𝐶 𝑘 . Por exemplo, se você gerar o particionamento 1+1+2+3+3 (para 𝑛=10), você geraria o contador {1:2, 2:1, 3:2}e determinaria o número de permutações como:

    5 𝐶 2 * 3 𝐶 1 * 2 𝐶 2

    5 aqui é o número de elementos no multiconjunto, contando as duplicatas. Ele é reduzido pelos elementos que você retira... etc. O último termo é sempre 1, pois resta apenas um número distinto para ele.

    Aqui está uma implementação:

    from math import comb
    
    def partition_counter(number: int) -> Counter:
        counter = [0] * (number + 1)
        result = {}
        
        def recur(number, n, take):
            if number == 0:
                # calculate number of unique permutations of this multiset:
                count = 1
                items = []
                for i, k in enumerate(counter):
                    if k:
                        count *= comb(n, k)
                        n -= k
                        items.append((i, k))
                result[tuple(items)] = count
                return
    
            if take > 1: # don't take any more occurrences of `take` 
                recur(number, n, take - 1)
            if take <= number: # do take another occurrence of `take` 
                counter[take] += 1
                recur(number - take, n + 1, take)
                counter[take] -= 1
                
    
        recur(number, 0, number)
        return result
    

    Isso produz o mesmo formato de saída da sua implementação de força bruta, mas mais rápido.

    • 1
  2. ravenspoint
    2025-04-15T04:43:26+08:002025-04-15T04:43:26+08:00

    Existem muitos algoritmos inteligentes para partição inteira, mas nenhum deles gera saídas no formato pretendido.

    Nesse caso, eu sugeriria usar um desses algoritmos e analisar a saída, convertendo-a para o formato desejado.

    Isso é rápido e simples, especialmente se você usar std::mapa biblioteca de modelos padrão C++.

    Aqui está um código C++ para fazer isso no seu exemplo de formato 'padrão' na sua pergunta

    main()
    {
        raven::set::cRunWatch::Start();
        raven::set::cRunWatch aWatcher("main");
    
        // read input
        read();
    
        // loop over input rows
        for (auto &row : theInput)
        {
            raven::set::cRunWatch aWatcher("ProcessLine");
    
            // construct row map
            cRowMap rowmap;
            for (int i : row)
                rowmap.add(i);
    
            // is row map unique?
            auto it = std::find(
                theRowMaps.begin(), theRowMaps.end(),
                rowmap);
            if (it == theRowMaps.end())
            {
                // add new unique row
                theRowMaps.push_back(rowmap);
            }
            else
            {
                // duplicate row, increment count
                it->inc();
            }
        }
    
        // display results
        for (auto &rm : theRowMaps)
            std::cout << rm.text() << "\n";
    
        raven::set::cRunWatch::Report();
    
        return 0;
    }
    

    Isto produz

    1 6, : 1
    1 4, 2 1, : 5
    1 3, 3 1, : 4
    1 2, 2 2, : 6
    1 2, 4 1, : 3
    1 1, 2 1, 3 1, : 6
    1 1, 5 1, : 2
    2 3, : 1
    2 1, 4 1, : 2
    3 2, : 1
    6 1, : 1
    raven::set::cRunWatch code timing profile
    Calls           Mean (secs)     Total           Scope
          32        2.2125e-06      7.08e-05        ProcessLine
    

    O desempenho é de 71 MICROssegundos para toda a entrada.

    Código completo em https://codeberg.org/JamesBremner/so79573357

    • -2

relate perguntas

  • Como divido o loop for em 3 quadros de dados individuais?

  • Como verificar se todas as colunas flutuantes em um Pandas DataFrame são aproximadamente iguais ou próximas

  • Como funciona o "load_dataset", já que não está detectando arquivos de exemplo?

  • Por que a comparação de string pandas.eval() retorna False

  • Python tkinter/ ttkboostrap dateentry não funciona quando no estado somente leitura

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve