Add iterations per cycle & optional sqrt optimization

This commit is contained in:
ItsDrike 2025-02-26 13:06:20 +01:00
parent e0178e27d2
commit 37234a44c9
Signed by: ItsDrike
GPG key ID: FA2745890B7048C0
3 changed files with 185 additions and 64 deletions

View file

@ -15,55 +15,85 @@ ApplicationWindow {
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
spacing: 10 spacing: 15
Text { GroupBox {
text: "Enter a number to factorize:" title: "Input"
font.pixelSize: 16
Layout.alignment: Qt.AlignHCenter
}
TextField {
id: numberInput
placeholderText: "Enter number"
// NOTE: We can't use the IntValidator here, since it doesn't support 64-bit values (long long)
Layout.fillWidth: true Layout.fillWidth: true
onTextChanged: {
// If the number changes, reset
factorizationController.reset();
}
}
RowLayout { ColumnLayout {
Layout.fillWidth: true spacing: 10
spacing: 10
Button { TextField {
text: { id: numberInput
if (factorizationController.isPaused) { placeholderText: "Enter a number"
return "Resume"; // NOTE: We can't use the IntValidator here, since it doesn't support 64-bit values (long long)
} else if (factorizationController.isRunning) { Layout.fillWidth: true
return "..."; font.pixelSize: 16
} else { onTextChanged: factorizationController.reset()
return "Start";
}
} }
Layout.fillWidth: true
enabled: !factorizationController.isRunning RowLayout {
onClicked: { Layout.fillWidth: true
if (factorizationController.isPaused) { Button {
factorizationController.resume(); text: factorizationController.isPaused ? "Resume" : factorizationController.isRunning ? "..." : "Start"
} else { Layout.fillWidth: true
factorizationController.start(parseInt(numberInput.text, 10)); enabled: !factorizationController.isRunning
onClicked: {
if (factorizationController.isPaused) {
factorizationController.resume();
} else {
factorizationController.start(parseInt(numberInput.text, 10));
}
}
}
Button {
text: "Pause"
Layout.fillWidth: true
enabled: factorizationController.isRunning
onClicked: factorizationController.stop()
} }
} }
} }
}
Button { GroupBox {
text: "Pause" title: "Settings"
Layout.fillWidth: true Layout.fillWidth: true
enabled: factorizationController.isRunning ColumnLayout {
onClicked: factorizationController.stop() spacing: 10
RowLayout {
Layout.fillWidth: true
Text {
text: "Iterations per cycle:"
Layout.alignment: Qt.AlignLeft
}
Slider {
id: iterationSlider
Layout.fillWidth: true
from: 1
to: 100000
stepSize: 1
value: 1
onValueChanged: factorizationController.iterationsPerCycle = value
}
Text {
text: iterationSlider.value.toFixed(0)
font.bold: true
Layout.alignment: Qt.AlignRight
}
}
CheckBox {
text: "Stop at sqrt(n)"
checked: true
onCheckedChanged: factorizationController.useSqrtOptimization = checked
}
} }
} }
@ -74,32 +104,34 @@ ApplicationWindow {
value: factorizationController.progress value: factorizationController.progress
} }
Text { GroupBox {
text: "Current Factor: " + factorizationController.currentFactor title: "Current Status"
font.pixelSize: 14 Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter ColumnLayout {
spacing: 5
Text {
text: "Current Factor: " + factorizationController.currentFactor
font.pixelSize: 14
Layout.alignment: Qt.AlignHCenter
}
}
} }
Text { GroupBox {
text: "Factors Found:" title: "Factors Found"
font.pixelSize: 14
Layout.alignment: Qt.AlignHCenter
}
ListView {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: factorizationController.factors
delegate: Text { Text {
text: modelData text: factorizationController.factors.join(", ")
font.pixelSize: 14 font.pixelSize: 14
horizontalAlignment: Text.AlignHCenter color: "black"
width: parent.width
} }
} }
Text { Text {
text: "Warning: This only shows factors below sqrt(num)" text: factorizationController.useSqrtOptimization ? "Note: Only factors up to sqrt(n) are shown." : "Note: Finding all factors (slower)."
font.pixelSize: 12 font.pixelSize: 12
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
} }

View file

@ -2,7 +2,8 @@
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
FactorizationController::FactorizationController(QObject* parent) : QObject{parent}, m_isRunning(false), m_currentFactNumber(0), m_currentFactor(2) { FactorizationController::FactorizationController(QObject* parent) :
QObject{parent}, m_isRunning(false), m_useSqrtOptimization(true), m_iterationsPerCycle(1), m_currentFactNumber(0), m_currentFactor(2) {
assert(connect(&m_timer, &QTimer::timeout, this, &FactorizationController::onTimerTick)); assert(connect(&m_timer, &QTimer::timeout, this, &FactorizationController::onTimerTick));
} }
@ -36,6 +37,31 @@ bool FactorizationController::isPaused() const {
return m_isPaused; return m_isPaused;
} }
bool FactorizationController::useSqrtOptimization() const {
return m_useSqrtOptimization;
}
void FactorizationController::setUseSqrtOptimization(bool value) {
if (value == m_useSqrtOptimization)
return;
m_useSqrtOptimization = value;
emit useSqrtOptimizationChanged();
reset();
}
int FactorizationController::iterationsPerCycle() const {
return m_iterationsPerCycle;
}
void FactorizationController::setIterationsPerCycle(int number) {
if (number == m_iterationsPerCycle)
return;
m_iterationsPerCycle = number;
emit iterationsPerCycleChanged();
}
long long FactorizationController::currentFactor() const { long long FactorizationController::currentFactor() const {
return m_currentFactor; return m_currentFactor;
} }
@ -48,8 +74,6 @@ void FactorizationController::start(long long number) {
m_originalNumber = number; m_originalNumber = number;
m_currentFactNumber = number; m_currentFactNumber = number;
m_currentFactor = 2; m_currentFactor = 2;
// we could also just compute this every time, but sqrt is pretty expensive
m_stopFactor = static_cast<long long>(std::sqrt(m_currentFactNumber));
m_isRunning = true; m_isRunning = true;
m_isFinished = false; m_isFinished = false;
m_factors.clear(); m_factors.clear();
@ -60,6 +84,13 @@ void FactorizationController::start(long long number) {
emit progressChanged(); emit progressChanged();
emit factorsChanged(); emit factorsChanged();
if (m_useSqrtOptimization) {
// we could also just compute this every time, but sqrt is pretty expensive
m_stopFactor = static_cast<long long>(std::sqrt(m_currentFactNumber));
} else {
m_stopFactor = m_currentFactNumber;
}
m_timer.start(0); m_timer.start(0);
} }
@ -108,7 +139,12 @@ void FactorizationController::reset() {
} }
void FactorizationController::onTimerTick() { void FactorizationController::onTimerTick() {
factorize(); for (int i = 0; i < m_iterationsPerCycle; i++) {
if (!m_isRunning)
break;
factorize();
}
} }
void FactorizationController::factorize() { void FactorizationController::factorize() {

View file

@ -3,6 +3,7 @@
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
#include <QVariant>
#include <qqmlintegration.h> #include <qqmlintegration.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
@ -11,8 +12,11 @@
* @brief A class that performs integer factorization in a step-by-step manner. * @brief A class that performs integer factorization in a step-by-step manner.
* *
* This class uses a timer to progressively factorize a given number, allowing * This class uses a timer to progressively factorize a given number, allowing
* for real-time updates on progress and discovered factors. It is designed * for real-time updates on progress and discovered factors. The computation will
* to be used in a Qt-based QML application. * run in a single thread, utilizing 0 tick timers to perform a small number of
* iterations on each cycle of the event loop.
*
* It is designed to be used in a Qt-based QML application.
*/ */
class FactorizationController : public QObject { class FactorizationController : public QObject {
Q_OBJECT Q_OBJECT
@ -20,6 +24,8 @@ class FactorizationController : public QObject {
Q_PROPERTY(bool isRunning READ isRunning NOTIFY isRunningChanged) Q_PROPERTY(bool isRunning READ isRunning NOTIFY isRunningChanged)
Q_PROPERTY(bool isFinished READ isFinished NOTIFY isFinishedChanged) Q_PROPERTY(bool isFinished READ isFinished NOTIFY isFinishedChanged)
Q_PROPERTY(bool isPaused READ isPaused NOTIFY isPausedChanged) Q_PROPERTY(bool isPaused READ isPaused NOTIFY isPausedChanged)
Q_PROPERTY(int iterationsPerCycle READ iterationsPerCycle WRITE setIterationsPerCycle NOTIFY iterationsPerCycleChanged)
Q_PROPERTY(bool useSqrtOptimization READ useSqrtOptimization WRITE setUseSqrtOptimization NOTIFY useSqrtOptimizationChanged)
Q_PROPERTY(long long number READ number NOTIFY numberChanged) Q_PROPERTY(long long number READ number NOTIFY numberChanged)
Q_PROPERTY(long long currentFactor READ currentFactor NOTIFY currentFactorChanged) Q_PROPERTY(long long currentFactor READ currentFactor NOTIFY currentFactorChanged)
Q_PROPERTY(int progress READ progress NOTIFY progressChanged) Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
@ -68,6 +74,18 @@ class FactorizationController : public QObject {
*/ */
bool isPaused() const; bool isPaused() const;
/**
* @brief Gets the number of iterations to be performed per cycle.
* @return The number of iterations performed per cycle.
*/
int iterationsPerCycle() const;
/**
* @brief Checks whether sqrt optimization is enabled.
* @return True if the sqrt optimization is enabled, otherwise False.
*/
bool useSqrtOptimization() const;
/** /**
* @brief Gets the original number being factorized. * @brief Gets the original number being factorized.
* @return The number being factorized. * @return The number being factorized.
@ -94,6 +112,37 @@ class FactorizationController : public QObject {
*/ */
QList<long long> factors() const; QList<long long> factors() const;
// endregion
// region: Setters
/**
* @brief Set the number of iterations per cycle.
*
* This is a way to speed up the computation, by increasing the amount of iterations
* performed per each computation (on every event loop cycle). Setting this to a number
* too high might lead to OS thinking the application is not responding, and the UI
* feeling slow. The optimal number depends on the speed of the CPU and the allocated
* CPU time to this process.
*
* This value can be changed even as a computation is ongoing.
*/
void setIterationsPerCycle(int number);
/**
* @brief Enable or disable using sqrt optimizations.
*
* When sqrt optimization is enabled, only factors up to sqrt of the original number
* will be checked. This allows the algorithm to finish quicker.
*
* If the intention is purely to check whether number is or isn't prime, this optimization
* is very useful. However, enabling it will lead to some factors not being found, so if
* you wish to perform a full factorization, leave this turned off.
*
* Note that changing this value while the computation is in progress will reset the
* computation.
*/
void setUseSqrtOptimization(bool value);
// endregion // endregion
// region: Functions // region: Functions
@ -135,11 +184,13 @@ class FactorizationController : public QObject {
signals: signals:
void isRunningChanged(); void isRunningChanged();
void isFinishedChanged(); void isFinishedChanged();
void isPausedChanged();
void useSqrtOptimizationChanged();
void iterationsPerCycleChanged();
void numberChanged(); void numberChanged();
void currentFactorChanged(); void currentFactorChanged();
void factorsChanged(); void factorsChanged();
void progressChanged(); void progressChanged();
void isPausedChanged();
private slots: private slots:
void onTimerTick(); void onTimerTick();
@ -148,6 +199,8 @@ class FactorizationController : public QObject {
bool m_isRunning; ///< Indicates whether factorization is active. bool m_isRunning; ///< Indicates whether factorization is active.
bool m_isFinished; ///< Indicates whether the factorization process is done. bool m_isFinished; ///< Indicates whether the factorization process is done.
bool m_isPaused; ///< Indicates whether the factorization process is paused (can be resumed). bool m_isPaused; ///< Indicates whether the factorization process is paused (can be resumed).
bool m_useSqrtOptimization; ///< Indicates whether to use the sqrt optimization
int m_iterationsPerCycle; ///< The number of iterations to perform per cycle.
long long m_currentFactNumber; ///< The number currently being factorized. long long m_currentFactNumber; ///< The number currently being factorized.
long long m_originalNumber; ///< The original input number. long long m_originalNumber; ///< The original input number.
long long m_currentFactor; ///< The current divisor being checked. long long m_currentFactor; ///< The current divisor being checked.