Home » excel » excel – Do-Loop with multiple conditions in VBA

excel – Do-Loop with multiple conditions in VBA

Posted by: admin May 14, 2020 Leave a comment

Questions:

This is my first question here and I must admit, I was not able to find any solution so far here or elsewhere. Here is the problem:

I wanted to program a code, which is supposed to do the following:

  • Ask the user how many rows are needed
  • Ask, how many columns are needed
  • Create a matrix, where in the first column all rows are numbered, in the first row all columns are numbered and within the matrix, all these numbers are the multiplied (row 3/column 4 contains 12, for example).
  • Finally, all prime numbers shall be colored.

My problem starts with the last step (in my code beginning at “‘Primzahlen hervorheben”). For each combination of row and column I use two for loops, in which I first assume, the product IS a prime number by setting primzahl = true and set k = 2, which is 1 upped through all numbers up to the product itself… BUT for some reason, k keeps growing and eventually reaches the five digits area, before it crashes. I guess, the problem must be in this line:

Do While (primzahl = True) Or (k <= i * j) Or (i * j <> 1)

I connected all conditions as one would in an if construct. The Editor itself does not seem to have a problem with this, but why does it ignore the second condition, which does not allow for k to become larger than i * j? Am I missing something?
Just to put it in words: The loop shall continue, as long as

  • it being not a prime number proves to be the case (primzahl = false), OR
  • k is smaller or equal to i * j, OR
  • i * j is not 1

This case is no emergency or anything, I just want to know, where the hook is. I would appreciate your support and hope, to get some helpful advise.

Sub rechnen()
    'Zellen leeren
      Cells.ClearContents
      Cells.ClearFormats

    'Variablen definieren
      Dim i As Integer, j As Integer, k As Integer, iMax As Integer, jMax As Integer, primzahl As Boolean

    'Variablen Werte zuweisen
      iMax = 3 'InputBox("Anzahl Zeilen")
      jMax = 5 'InputBox("Anzahl Spalten")

    'Eigentliche Prozedur
      For i = 1 To iMax
        Cells(1 + i, 1).Value = i
        For j = 1 To jMax
          Cells(1, 1 + j).Value = j
          Cells(1 + i, 1 + j).Value = i * j
        Next j
      Next i

    'Spaltenbreite anpassen
      ActiveSheet.UsedRange.Columns.AutoFit



    'Primzahlen hervorheben
      For i = 1 To iMax
        For j = 1 To jMax
          k = 2
          primzahl = True
          ''''''''
          Do While (primzahl = True) Or (k <= i * j) Or (i * j <> 1)
            If Cells(1 + i, 1 + j).Value Mod k = 0 Then
              primzahl = False
              Exit Do
            End If
              k = k + 1
          Loop
          ''''''''
          If primzahl = True Then
            Cells(1 + i, 1 + j).Interior.Color = vbRed
          End If      
        Next j
      Next i

End Sub
``
How to&Answers:

You seem to be mixing up OR and AND:

The loop shall continue, as long as […] OR […] OR […]

You actually mean to say that the loop shall end when either one of these three conditions is not true. So logically the loop should continue while all of the conditions are still true. This expresses a logical AND, not an OR.

So you meant:

Do While (primzahl = True) And (k <= i * j) And (i * j <> 1)

But this can be simplified.

The expression primzahl = True will always be true, because as soon as you set primzahl = False, you explicitly exit the loop with Exit Do, so the While condition never gets evaluated with primzahl = False.

Secondly, if (i * j <> 1) is true the first iteration, it will also be true in any other iteration, since the iterations do not change i nor j. But even if it were true the first time, then certainly k <= i * j is not true (you already set k = 2 and it only gets greater values), so also this condition is always going to be true once you get past the condition on k.

So, taking that into account you would have:

k = 2
Do While k <= i * j
    ' etc..
    k = k + 1
Loop

And then why not make it a For loop, since you let k go from 2 to i*j?

For k = 2 To i * j
    ' etc.. (don't do k = k + 1 now)
Next

Concerning the assignment you gave yourself: numbers resulting from i * j with i and j both greater than 1 are by definition not primes, so there is no sense in performing a check on them. They have at least i and j as divisors.

Answer:

I have finally returned, after I was absent for quite a while. With a fresh mind I just tried again to tackle the task described above. Naturally, I implemented certain changes:

  • First: The numbers in the matrix are no longer products of the given numbers in the first row and column, since that fully defeats the purpose of checking for prime numbers. Instead they are now random numbers, ranging from 1 to 10, to keep it simple.

  • Second: What my code now does , is for each cell (after receiving a random number) to FIRST ASSUME the number in question IS a prime number and color the cell red. This assumption is furthermore tested by dividing the given number with 2 then 3 then…. up to the given number-1. If one of those checks proves to be positive, i.e. the mod is equal to zero, the format of the cell is cleared and the next cell undergoes the same mechanism.
    Just wanted to share this insight 🙂

Sub matrix()
Cells.ClearContents
Cells.ClearFormats
Dim xMax As Integer, yMax As Integer, x As Integer, y As Integer, z As Integer
xMax = InputBox("Anzahl Spalten")
yMax = InputBox("Anzahl Spalten")
Randomize
    For x = 1 To xMax
        Cells(1 + x, 1) = x
        For y = 1 To yMax
            Cells(1, 1 + y) = y
            Cells(1 + x, 1 + y) = Int(Rnd() * 10 + 1)
            z = 2
            Do While z < Cells(1 + x, 1 + y).Value
                Cells(1 + x, 1 + y).Select
                ActiveCell.Interior.Color = vbRed
                If Cells(1 + x, 1 + y) Mod z = 0 Then
                    Cells(1 + x, 1 + y).Select
                    ActiveCell.ClearFormats
                    Exit Do
                End If
                z = z + 1
            Loop
        Next y
    Next x

End Sub

Answer:

Just found another problem: 2s are ignored, since the loop only works, if z is < 2. However, I corrected this by simply check if the number is two before the loop. If this is the case, the loop will be skipped:

Sub matrix()
Cells.ClearContents
Cells.ClearFormats
Dim xMax As Integer, yMax As Integer, x As Integer, y As Integer, z As Integer
xMax = 10 'InputBox("Anzahl Spalten")
yMax = 10 'InputBox("Anzahl Spalten")
Randomize
For x = 1 To xMax
Cells(1 + x, 1) = x
For y = 1 To yMax
Cells(1, 1 + y) = y
Cells(1 + x, 1 + y) = Int(Rnd() * 10 + 1)
If Cells(1 + x, 1 + y).Value = 2 Then
Cells(1 + x, 1 + y).Interior.Color = vbRed
GoTo Skip
End If
z = 2
Do While (z < Cells(1 + x, 1 + y).Value)
Cells(1 + x, 1 + y).Interior.Color = vbRed
If (Cells(1 + x, 1 + y).Value Mod z = 0) And (Cells(1 + x, 1 + y).Value <> 2) Then
Cells(1 + x, 1 + y).ClearFormats
Exit Do
End If
z = z + 1
Loop
Skip:
Next y
Next x
End Sub