C++ function to tell whether a group is cyclic

In summary: G##, then no power of ##e## can generate ##G## either. Proof: if ##\langle e\rangle## is the subgroup generated by ##e##, then ##e^n \in \langle e \rangle## and so ##\langle e^n \rangle \subset \langle e \rangle##. So you don't have to test all of the elements.2. Why don't you break out of the for loop when you set foundGenerator = true? [edit I just noticed the !foundGenerator condition on the for loop - I hadn't scrolled far enough to the right to notice it at first. So disregard this observation.]I haven't checked your logic carefully, so I
  • #1
Jamin2112
986
12
Is there anything wrong with my logic and is there any way to further optimize this potentially long-running function? I've put a lot of comments to explain what's going on.

Code:
template <typename ObType, typename BinaryFunction>
bool isCyclic(const std::set<ObType> & G, BinaryFunction & op, ObType & g)
{
	/*
	isCyclic returns true or false depending on whether the group G
	is an cyclic group, that is, whether it can be generated from
	one its elements. Formally, G is cyclic if there exists an 
	element g in G such that, for all elements h in g, 
	h = g^n for some n. 
	*/
	bool foundGenerator = false;
	size_t maxN = G.size();
	/*
	Guaranteed that for each x in G, x^(maxN + 1) will be in the set
	{x, x^2, ..., x^maxN}. That is the meaning of maxN. 
	*/
	
	for (typename std::set<ObType>::const_iterator it(G.cbegin()), offend(G.cend()); it != offend && !foundGenerator; ++it)
	{
		ObType e0 = *it; 
		ObType e = e0;
		std::set<ObType> powSet = {e};
		size_t k = 2;
		while (k <= maxN)
		{
			e = op(e, e0); /* Sets e equal to e^i */
			if (powSet.count(e) == 0)
				powSet.insert(e);
			else
				break;
			/* Why the break statement? 
			   If e is already in powSet, then we've found a cycle but 
			   powSet != G; therefore e cannot generate G.
			*/
			++k;
		}
		if (k == maxN) /* true iff (powSet == G) */
		{
			foundGenerator = true;
			g = e;
		}
	}
	return foundGenerator;
}
 
Last edited:
Technology news on Phys.org
  • #2
A couple of observations:

1. Your algorithm could be more efficient. If ##e## does not generate ##G##, then no power of ##e## can generate ##G## either. Proof: if ##\langle e\rangle## is the subgroup generated by ##e##, then ##e^n \in \langle e \rangle## and so ##\langle e^n \rangle \subset \langle e \rangle##. So you don't have to test all of the elements.

2. Why don't you break out of the for loop when you set foundGenerator = true? [edit I just noticed the !foundGenerator condition on the for loop - I hadn't scrolled far enough to the right to notice it at first. So disregard this observation.]

I haven't checked your logic carefully, so I don't claim that these are the only problems. :-p
 
Last edited:
  • #3
Here is another optimization. Let ##|G|## denote the size (order) of ##G##. Then for any element, ##|\langle e \rangle|## must be a divisor of ##|G|##. So, in order to determine whether ##e## is a generator, you just need to check ##e^n## for each divisor ##n## of ##|G|##. So let ##n## be the smallest divisor of ##|G|## such that ##e^n## equals the identity, or equivalently, ##e^{n+1} = e##. There are only two possibilities:

1. ##n## is a proper divisor of ##|G|##, in which case ##e## does not generate ##G##

2. ##n = |G|##, in which case ##e## generates ##G##
 
Last edited:
  • #4
[edited previous post for clarity]
 
  • #5
jbunniii said:
just need to check ##e^n## for each divisor ##n## of ##|G|##.
Isn't there a potential issue in the case ##e^{(n/k)}## % G == 1, where k could be 2, 3, ... ? I don't know if there is a fast way to determine the smallest m such that ##e^m## % G == 1.

Also, is this potential cyclic field based on a prime number, a Galios field, a Galios binary field, or something else?
 
Last edited:
  • #6
rcgldr said:
Isn't there a potential issue in the case ##e^{(n/k)}## % G == 1, where k could be 2, 3, ... ? I don't know if there is a fast way to determine the smallest n such that ##e^n## % G == 1.
I'm not sure what you mean with the notation ##e^{(n/k)}## % G == 1. In general, a group only has one operation, multiplication. (Exponentiation is just repeated multiplication.) What do you mean by the % operation?

In any case, he doesn't need to find the smallest positive ##n## such that ##e^n = 1##, he just needs to determine whether there are any such ##n## which are smaller than (hence strict divisors of) ##|G|##. This is true if and only if the element is NOT a generator. A brute force way to do this would be (pardon my pseudocode):

Code:
isGenerator = true; // until proven otherwise
for (n = 1; n <= N/2; n++) // N = |G|; we only need to loop to N/2 since this is the largest possible strict divisor of N
{
    if (n is not a divisor of N)
    {
        continue;
    }
    if (e^n == 1)
    {
        isGenerator = false;
        break;
    }
}
[edit] OK, I think I see your point: it might be more work to check "if (n is not a divisor of N)" than simply to check "if (e^n == 1)" for every n, in which case it might actually be faster without the first "if" block.

Also, is this potential cyclic field based on a normal prime number integer, or Galios prime polynomial, or something else?
I've been assuming that it's just an arbitrary finite group, and that the OP has a C++ class which "implements" the group, i.e. a finite set and a binary operation on the set which obey the group axioms. Jamin - is that correct?
 
Last edited:
  • #7
It appears that the goal here is to find at least a muliplicative cyclic group or possibly a finite field where addition, multiplication and their inverses work with a finite set of elements. I did some testing with a binary Galios field composed of 8 bit elements modulo the thirty possible 9 bit "prime" polynomials. A generator e raised to m = ## 2^8 ## - 1 = 255 results in ##e^m## % G == 1. The largest value I could get for m for a non-generator e such that ##e^m## % G == 1, was m = 85 = 255/3. So in the case of binary fields, jbunniii's for loop might be able to stop at n <= N/3, and n <= N/2 should be safe for most types of fields.
 
Last edited:
  • #8
jbunniii said:
A couple of observations:

1. Your algorithm could be more efficient. If ##e## does not generate ##G##, then no power of ##e## can generate ##G## either. Proof: if ##\langle e\rangle## is the subgroup generated by ##e##, then ##e^n \in \langle e \rangle## and so ##\langle e^n \rangle \subset \langle e \rangle##. So you don't have to test all of the elements.


Updated it to incorporate that:

Code:
template <typename ObType, typename BinaryFunction>
bool GroupTheorizer::isCyclic(std::set<ObType> G, BinaryFunction & op, ObType & g) const
{
	/*
	isCyclic returns true or false depending on whether the group G
	is an cyclic group, that is, whether it can be generated from
	one its elements. Formally, G is cyclic if there exists an 
	element g in G such that, for all elements h in g, 
	h = g^n for some n. 
	*/
	bool foundGenerator = false;
	size_t maxN = G.size();
	/*
	Guaranteed that for each x in G, x^(maxN + 1) will be in the set
	{x, x^2, ..., x^maxN}. That is the meaning of maxN. 
	*/
	for (typename std::set<ObType>::iterator it(G.begin()); it != G.cend() && !foundGenerator; ++it)
	{
		ObType e0 = *it; 
		ObType e = e0;
		std::set<ObType> powSet = {e};
		size_t k = 2;
		while (k <= maxN)
		{
			e = op(e, e0); /* Sets e equal to e^i */
			if (powSet.count(e) == 0)
				powSet.insert(e);
			else
				break;
			/* Why the break statement? 
			   If e is already in powSet, then we've found a cycle but 
			   powSet != G; therefore e cannot generate G.
			*/
			++k;
		}
		if (k == maxN) /* true iff (powSet == G) */
		{
			foundGenerator = true;
			g = e;
		}
		else
		{
			for (std::set<ObType>::iterator i(powSet.begin()), j(powSet.end()); i != j; ++i)
				G.erase(*i)
				/* Why? 
				   powSet is the set of all powers of e0. If e0 does not generate G, then neither
				   does any of its powers. 
				*/
		}
	}
	return foundGenerator;
}
 
  • #9
For most fields, even if e is not a generator, it should cycle through a sub-set of the elements. The inner loop could be changed to something like this:

Code:
        e = e0
        for(k = 2; k <= maxN; ++k)
        {
            e = op(e, e0);   // Sets e equal to e^k
            if(e == e0)      // break if e cycled back to e0
                break;
        }
        if(k == (maxN-1))    // maxN-1 == # of non-zero elements in the group
        {
            // found a generator
        }
 
Last edited:

Related to C++ function to tell whether a group is cyclic

1. What is a cyclic group in C++?

A cyclic group in C++ is a mathematical concept that refers to a set of elements that can be generated by repeatedly applying a single operation to a starting element. In other words, a cyclic group is a group where every element can be expressed as a power of a specific element called a generator.

2. How do I determine if a group is cyclic in C++?

To determine if a group is cyclic in C++, you can use a function called is_cyclic(). This function takes in a group as its input and returns true if the group is cyclic, and false otherwise. The function works by checking if all elements in the group can be generated by repeatedly applying a single operation to a starting element.

3. Can a non-numeric group be cyclic in C++?

Yes, a non-numeric group can also be cyclic in C++. This is because the concept of a cyclic group is not limited to just numbers. In C++, any type of group that satisfies the definition of a cyclic group can be considered cyclic.

4. What is the time complexity of the is_cyclic() function in C++?

The time complexity of the is_cyclic() function in C++ is O(n), where n is the number of elements in the group. This is because the function needs to check each element in the group to determine if it can be generated by repeatedly applying a single operation to a starting element.

5. Are there any other functions in C++ related to cyclic groups?

Yes, there are other functions in C++ that are related to cyclic groups, such as get_generator(), which returns the generator element of a given cyclic group, and get_order(), which returns the order (number of elements) of a given cyclic group. These functions can be useful for further analysis or manipulation of cyclic groups in C++.

Similar threads

  • Linear and Abstract Algebra
Replies
2
Views
1K
  • Linear and Abstract Algebra
Replies
1
Views
1K
  • Calculus and Beyond Homework Help
Replies
1
Views
1K
  • Advanced Physics Homework Help
Replies
2
Views
1K
  • Programming and Computer Science
Replies
4
Views
3K
  • Calculus and Beyond Homework Help
Replies
14
Views
6K
  • Calculus and Beyond Homework Help
Replies
3
Views
1K
  • Calculus and Beyond Homework Help
Replies
2
Views
2K
  • Calculus and Beyond Homework Help
Replies
6
Views
4K
  • Calculus and Beyond Homework Help
Replies
4
Views
2K
Back
Top